不同于双向绑定,单向绑定更多的是从性能优化的角度出发的。
我们知道,模板依赖于数据才能渲染,当数据更新时,模板理应得到更新,但这里存在一个问题,模板是全量更新还是局部更新呢?
如果没有单向绑定,毫无疑问,模板只能全量更新,因为它不知道变化的数据对应模板的什么部分。当模板比较复杂时,比如渲染一个大列表,全量更新的性能消耗非常严重,甚至可能带来明显的丢帧。
因此,我们特地设计了单向绑定指令,绑定方式有三种:
- 绑定 DOM 元素的 Attribute
- 绑定 DOM 元素的 Property
- 绑定组件的 Property
绑定 DOM 元素的 Attribute
<div class="{{className}}">
绑定 className 数据到 class 属性
</div>
当 DOM 元素的 attribute value 有且只有一个表达式,并且这个表达式支持预分析 keypath
时,会自动生成一个属性绑定。
如示例中的 className
会绑定到 class
这个 attribute,当 className
变化时,仅会带来一次 setAttribute
操作,代价非常小。
绑定 DOM 元素的 Property
目前仅支持两种 property,即 innerHTML
和 innerText
。
<!-- html 绑定到 innerHTML -->
<div>
{{{html}}}
</div>
<!-- text 绑定到 innerText -->
<div>
{{text}}
</div>
需要注意的是,元素的子元素有且只能有一个表达式,并且这个表达式支持预分析 keypath
。
绑定组件的 Property
组件和元素很类似,因此绑定组件的 Property 和绑定 DOM 元素的 Attribute 条件是相同的。
此外,组件还支持延展属性传值,延展出来的属性,同样会自动生成属性绑定,但有一个前提,延展的表达式支持预分析 keypath
。
预分析 Keypath 的表达式
以上三种绑定方式的前提都是表达式支持预分析 keypath。简单的说,支持预分析 keypath 的表达式只有以下两种:
- 标识符,或者说变量
- 属性访问,或者说访问对象的属性或数组的下标
// 标识符
{{user}}
{{name}}
// 属性访问
{{user.name}}
{{users.0.name}}
// 举一个反例
{{users[ index ].name}}
如果表达式在编译阶段就能分析出 keypath
,我们认为它是可绑定的,否则不能绑定。
反例中,keypath
还依赖于运行时的 index
,因此我们无法确定 keypath
是 users.0.name
还是 users.1.name
。
支持预分析 keypath
的表达式,换句话说,这个表达式是静态的,因此我们可以监听 keypath
的变化。
如果表达式不是静态的,在运行时会充满各种可能性,增加不可预知的风险,为了保持简单,我们就这么愉快的决定了。