# 组件

在RCRE中，组件使用一个{}来表示，比如一个用于表述文本的组件就可以使用下面的JSON结构来描述：

```_raw_json
{
    "type": "text"
}
```

JSON上面的属性，会直接作为组件的属性来进行使用，不同的组件会需要不同的JSON属性来完成不同的功能。

## 如何渲染组件
在RCRE中，渲染组件是通过开发者编写JSON语法结构的描述文件来完成的。

开发者在编写好JSON配置文件之后，可以通过调用RCRE的API来实现组件的渲染。

### 配置文件的基本结构

每一个JSON配置文件都需要遵循以下的结构来完成。

```_raw_json
{
    "body": [

    ]
}
```

所有的配置文件属性都需要放置在body属性内部。


## 组件的类型

在RCRE中，组件又分为两种类型，无状态组件和有状态组件。

### 无状态组件
在RCRE中，除了container组件之外的其他组件都是无状态组件。开发者只需要把container组件和其他组件分开来看待即可。

无状态组件和有状态组件的唯一区别就在于无状态组件不会持有私有的数据模型，它所需要的任何数据，都会通过一种特殊的方式，从父级组件中的有状态组件上获取。

而这个特殊的方式，也就是下面章节会介绍的ExpressionString。

现在，我们来一个无状态组件最简单的例子：

```json
{
    "body": [{
        "type": "text",
        "text": "helloworld"
    }]
}
```

不管是无状态组件还是有状态组件，任何一个组件都必然会包含`type`这个属性。`type`属性代表着组件的类型。

比如`type`为container的组件就是container组件，`type`为text的组件就是Text文本组件。

上面的例子描述渲染引擎需要渲染一个Text文本组件。通过查阅文档提供的[Text组件文档](./component/text)就会发现，

Text组件需要一个text属性的值来描述要展示的文字。因此就需要添加一个text属性，并填写上需要的值。

本节仅作为一个抛砖引玉，有兴趣的读者可以参考RCRE提供的各种各样的无状态组件的文档来实现业务需要的不同功能。

接下来，需要重点介绍有状态组件的内容。

### 有状态组件
虽然开发者写的仅仅是配置文件，还是每一个开发者都要随时留意当前应用程序数据状态的变更。

使用RCRE来开发应用程序是面向数据的一种思考方式。

关于什么是面向数据编程，请看[面向数据编程的四个基本公式]()

RCRE中只有一个有状态组件，container组件。

每一个有状态组件都会持有私有的数据模型，RCRE会管理整个应用中所有的container组件的数据模型。

在整个应用程序中，会存在很多个有状态组件, 每个有状态之间有可能会出现嵌套，关于嵌套的container组件中的一个逻辑，请看[嵌套的container组件](./guide/nestContainer)

### model属性

整个应用中会存在很多个container组件，而且每个container组件所持有的数据模型都不相同，而且也不会相互影响。

所以在声明每个container组件的时候，就必须提供一个唯一的Key来把不同的container组件的数据模型区分开来。

因此每个container组件都必须提供model属性来标识这个container组件唯一的Key值。

model属性可以是任意的一个不重复的字符串。

### children属性
如果页面中只存在有状态组件，而没有一个无状态组件的话，那么有状态组件所持有的数据对象也将会毫无用处。

有状态组件的私有数据模型存在的意义，就是给子级所有的无状态组件提供数据。

理解上面这点，children属性就很容易解释了。

children属性是一个数组类型，数组中每一个元素都将是一个组件。

而通过container组件的children属性渲染出来的无状态组件，都可以直接读取到container组件自身的私有数据模型。

但是需要注意的是，上面描述的读取代表的已经具备读取这样的功能，但是，读取哪一个属性，最终又如何把读取到的值作为无状态组件新的值却是又另外一套机制决定的。

而这套机制，将会是下一个章节会介绍的ExpressionString。

这里，先给一些简单的介绍。

### ExpressionString简单介绍

无状态组件不会自动同步有状态组件的值，如果需要进行同步的话，就需要在无状态组件的指定属性上，使用ExpressString这个功能来进行赋值操作。

ExpressionString是嵌入在字符串属性值中，以#ES为前缀，再辅以一对大括号，在大括号内部的字符串是一段类似JavaScript的表达式。使用这样的方式，

就可以指定当前无状态组件会从父级的有状态组件中读取什么样的值。

假如现在container组件中已经存在如下的值：

```_raw_json
{
    "name": "andycall"
}
```

在ExpressionString中，container组件的数据模型可以通过$data这个变量来获取，

所以读取父级container组件值的方式就只需要`#ES{$data.name}`就可以完成。

### data属性
在默认情况下，container组件中的值仅仅只是一个空对象。如果需要在初始化的时候，指定一些默认的值，就需要使用data属性来设置一些默认值。

data属性是一个对象属性，对象中所有的值都会在初始化阶段，写入到container组件的数据模型中。
### 控制组件是否渲染
每个无状态组件，都可以通过hide和show变量来控制变量是否渲染出来。设置hide:true或者show:false组件就会隐藏。

```json
{
    "body": [{
        "type": "container",
        "model": "hiddenExample",
        "data": {
            "hidden": false
        },
        "children": [{
            "type": "checkbox",
            "text": "隐藏",
            "name": "hidden"
        }, {
            "type": "text",
            "text": "这是一个文本",
            "hidden": "#ES{$data.hidden}"
        }, {
            "type": "text",
            "text": "这是另外一个文本",
            "show": "#ES{$data.hidden}"
        }]
    }]
}
```

### 一个小例子

下面，我们把最上面的helloworld例子进行一些改造，把原本直接写在Text组件上的text属性提取到container组件上面来。

话不多说，直接上例子：

```json
{
    "body": [{
        "type": "container",
        "model": "DEMO",
        "data": {
            "name": "andycall"
        },
        "children": [{
            "type": "text",
            "text": "#ES{$data.name}"
        }]
    }]
}
```

### 使用开发工具来查看container组件中的数据模型

RCRE底层使用Redux来管理container组件的数据模型，因此可以直接使用Redux DevTools来查看目前数据模型所带有的值。

安装Redux DevTools需要在Chrome浏览器商店上，通过这个[下载地址](https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd?utm_source=chrome-ntp-icon)进行下载安装.

安装完成之后， 需要在 `chrome://extensions/`中， 把Redux DevTools的访问本地文件的权限打开， 这样才能在直接打开单个html文件情况下， 使用redux devtools。

![QQ20171017-211321](https://ws2.sinaimg.cn/large/006tKfTcly1fkljye5nrlj30lo05qq3i.jpg)

之后Redux将会出现在Chrome的控制台中。 这样我们就可以通过Redux这个工具来查看当前数据模型中的值

![QQ20171017-211733](https://ws4.sinaimg.cn/large/006tKfTcly1fklk1sq8z1j30uy07xjrr.jpg)

