发布于 2015-09-06 14:49:29 | 2927 次阅读 | 评论: 0 | 来源: 网络整理
事实上,regularjs的数据绑定实现基于基于脏检查. 它非常接近于angularjs
脏检查即一种不关心你如何以及何时改变的数据, 只关心在特定的检查阶段数据是否改变的数据监听技术.
本节包含内容
这里会统一使用angularjs生态圈的术语,方便开发者理解
相信对于熟悉angularjs的开发者而言, 脏检查这个概念不会太陌生.
以插值{post.title}
为例, 当regularjs在compile阶段遇到这个语法数元素时, 内部的处理逻辑如下.
walkers.expression = function( ast ){
var node = document.createTextNode("");
this.$watch(ast, function(newval){
dom.text(node, "" + (newval == null? "": "" + newval) );
})
return node;
}
这段代码很好理解,即遇到插值时,我会创建一个textNode, 每当数据变化时,修改textNode的textContent值.
其中
post.title
, 反映在组件就是component.data.post.title.那么问题来了, 怎么判断值发生改变了?
首先, 上例通过$watch
接口产生的watcher对象看起来是这样的
{
get: function(context){...} //获得表达式当前求值, 此函数在解析时,已经生成
set: function(){} // 有些表达式可以生成set函数, 用于处理赋值, 这个一般用于双向绑定的场景
once: false // 此监听器是否只生效一次
last: undefined// 上一次表达式的求值结果
fn: function(newvalue, oldvalue){} // 即你传入$watch的第二个参数, 当值改变时, 会调用此函数
// ...
}
然后我们要提到内部的一个非常重要的阶段——digest阶段, 当系统进入此阶段时,将会进行数据检查, 它的处理流程如下:
dirty = false
component.$watch
绑定的数据观察者watcher, 对比当前值watcher.get(component)
与老值watcher.last
watcher.fn(newvalue, oldvalue)
, 并导致__dirty__=true
. 设置watcher.last=newvalue
dirty===true
, 我们重新进入步骤1. 否则进入步骤4.好, 现在我们了解数据检查的内部流程了, 但是何时进入digest阶段
有基本的javascript基础的同学都知道, 在姗姗不来的ES7Object.observe
方法没有到来时(但是别对Object.observe
抱太大得期望, 它不会改变目前类mvvm框架的格局, 我后续会写一篇文章说明), 当我们对对象属性进行赋值时, 我们是无法知道数据发生改变了的, 所以digest阶段必然是 主动进入的 .
在regularjs中, digest阶段是由$update
方法触发的.
Example
var component = new Regular();
component.data.name = 'leeluolee'
// you need call $update to Synchronize data and view
component.$update();
值得庆幸的是,大部分情况下都会自动进入digest阶段.比如事件、timeout模块等等.
<div on-click={blog.title='Hello'}>{blog.title}</div>
当点击节点后, 内容区会变成Hello.
但是很显然, 脏检查是低效的, 它的效率基本上取决于你绑定的观察者数量, 在regularjs中, 你可以通过@(Expression)
元素来控制你的观察者数量.
然而结合这种类mvvm系统中, 他又是高效的. 因为监听模式带来了dom的局部更新, 而dom操作恰恰又是隐藏的性能瓶颈所在.
regularjs实际上在解析时,已经提取了表达式的依赖关系, 在未来Observe到来时, 可以调整为脏检查 + 依赖计算的方式