# 数据同步

> 有状态组件和无状态组件之间的数据同步是通过ExpressionString来实现的

如果你还不清楚有状态组件和无状态组件是什么，那么请看上一节，[组件](./guide/containerComponent)

本节将从上一节的最后一个例子来逐步展开介绍

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

### 例子解读

开头的例子中，使用了`#ES{$data.name}`来访问有状态组件中的name属性值。$data可以看作是有状态对象的引用。


## 语法

ExpressionString必须以`#ES`为开头，然后后面紧接着一组大括号。大括号内部为ExpressString的表达式内容。

ExpressionString的表达式会运行一个精简版的Javascript执行环境中。在这个环境内, 只能执行任何和赋值有关的Javascript代码，比如读取一个对象，数组的值，

进行四则运算，调用导入的filter函数等。除此之外，像JavaScript的其他功能，例如函数，作用域，闭包，变量声明等功能都将不会支持。这些JavaScript原有的功能不仅不能减少ExpressionString的使用成本和运行效率，而且会带来很多安全性风险。

不过有一个特例，虽然ExpressionString中无法声明函数，但是可以调用函数。

开发者可以通过使用Filter这个功能，在引擎执行之前导入开发者自己编写的一些函数，就可以在任意一个ExpressionString中进行调用。


### 基本使用

ExpressionString是一个精简版的JavaScript执行环境。所以一些JavaScript的功能都可以直接使用。

下面是ExpressionString可以使用的函数和对象一览。

| JavaScript的功能                            | 是否支持 | 样例                            |
| ---------------------------------------- | ---- | ----------------------------- |
| String函数                                 | 是    | String.prototype.toString     |
| Number函数                                 | 是    | Number("111")                 |
| Boolean函数                                | 是    | Boolean('tru')                |
| JSON对象                                   | 是    | JSON.parse('[{"name": "1"}]') |
| Math对象                                   | 是    | Math.floor(1.2)               |
| Date对象                                   | 是    | Date.now()                    |
| parseInt, parseFloat, encodeURI, decodeURI | 是    | parseInt("1.2")               |

### 字符串模板

Expression String 一样也能当作是字符串模板来使用。  这在使用变量来拼接一个字符串是非常有效的方式

```text
you are the #ES{1 + 1}th ==> you are the 2th
```

解析器只会处理在`#ES`内部的代码。 其他的字符串都只会当作是普通字符串进行拼接。

## RCRE内置变量

在container组件内部，在任何地方使用ExpressString都可以访问当前container组件所提供的内置变量。

container组件提供的内置变量有：

+ $data 当前container组件的数据模型
+ $trigger 组件内触发器传递过来的数据对象
+ $query 浏览器地址栏的数据
+ $global 调用RCRE传入的全局对象
+ $now moment()返回的当前时间对象
+ $moment moment.js函数 

RCRE提供了一些内置的变量, 来方便开发者在Expression String中对当前`container`组件的数据模型进行操作。

### $data

`$data`变量提供了在Expression String获取当前数据模型数据的功能。 `$data`是一个普通的javaScript对象。 其中的各个属性可以直接在Redux Devtools进行查看。 例如:

```json
{
    "body": [
        {
            "type": "container",
            "model": "demo2",
            "data": {
                "name": "andycall",
                "age": "21"
            },
            "children": [
                {
                    "type": "text",
                    "text": "my name is #ES{$data.name}"
                },
                {
                    "type": "text",
                    "text": "andycall's age is #ES{$data.age}"
                }
            ]
        }   
    ]
}
```

container组件上的data属性表示是组件初始化时的默认值，所以这个container组件中的数据模型的值为

```_raw_json
{
    "name": "andycall",
    "age": "21"
}
```

所以container组件渲染字级的2个text组件的时候，都可以去访问初始化时写入的值。

### $trigger

$trigger是来自组件内部事件触发的时候传递过来的值，举个例子，点击一个按钮，把当前数据模型中的count的值加一。

```json
{
    "body": [
        {
            "type": "container",
            "model": "triggerExample",
            "data": {
                "count": 0
            },
            "dataCustomer": {
                "customers": [
                    {
                        "mode": "pass",
                        "name": "updateCount",
                        "config": {
                            "model": "triggerExample",
                            "assign": "#ES{$trigger.updateCount}"
                        }
                    }
                ]
            },
            "children": [
                {
                    "type": "text",
                    "text": "now count is: #ES{$data.count}"
                },
                {
                    "type": "button",
                    "text": "click to add",
                    "trigger": [
                        {
                            "event": "onClick",
                            "targetCustomer": "updateCount",
                            "params": {
                                "count": "#ES{$data.count + 1}"
                            }
                        }
                    ]
                }
            ]
        }
    ]
}
```

## $query
如果地址栏有查询的数据，就可以通过$query来读取。

```json
{
    "body": [
        {
            "type": "container",
            "model": "queryExample",
            "children": [
                {
                    "type": "text",
                    "text": "querystring value is: #ES{$query}"
                },
                {
                    "type": "text",
                    "textType": "link",
                    "style": {
                        "display": "block"
                    },
                    "text": "click to add ?name=andycall&age=21",
                    "href": "/guide/expressionString?name=andycall&age=21"
                }
            ]
        }
    ]
}
```

## $global
在使用调用RCRE的时候，除了code属性之外，还可以传递global属性来设置全局变量。

```javascript
var engine = React.createElement(RCRE.Render, {
    code: JSON.stringify(config),
    global: {
        text: "this is global value"
    }
});
```

```json
{
    "body": [
        {
            "type": "text",
            "text": "global value is #ES{$global}"
        }
    ]
}
```

## $now

直接使用$now可以获取当前时间

```json
{
    "body": [
        {
            "type": "text",
            "text": "now timestamp is #ES{$now}"
        }
    ]
}
```

## $moment

可以使用[moment.js](https://momentjs.com/) API来获得更多时间相关的方法

```json
{
    "body": [
        {
            "type": "text",
            "text": "now time is #ES{$moment().format('YYYY-MM-DD')}"
        }
    ]
}
```

### ExpressionString 支持的语法规则一览

不同于常规的编程语言，ExpressionString就是为赋值而生，所以现在以后也不会支持和赋值没有任何关系的语法功能。

已支持的功能和javascript的完全保持一致，比如 `NaN === NaN` 会返回false。

目前支持的语法功能如下：

1. 纯字面量四则运算
2. 访问已有上下文的变量，比如$data,$trigger
3. 对象属性访问，数组值访问，数组，对象内置的一些函数，原型方法
4. call和apply函数也支持。支持Array.prototype.slice.call等
5. 可以使用固定的值，比如 1,2,3和\[1,2,3,4], 或者是{name: 1, age: 2} 
6. 逻辑表达式，三元运算符， && || !== === 
  7, new 运算符，可以把一个class 当作是自定义函数来使用。
