MVVM双向数据绑定的个人理解
AngularJS 的脏检查机制
AngularJS的双向数据绑定采用的脏检查机制,所谓“脏检查”,就是检测到数据与之前不一致,有变化,这时候就认为数据是“脏”的,然后把不一致的数据变更为较新的值,直到没有新的数据变动。
通常UI事件、ajax请求后的数据处理 或者 timeout 延迟事件会触发这个脏检查这个过程的实现,主要是靠 $watch
和$digest
两个重要的函数。
$watch
每当有变量通过$scope
对象和页面UI绑定,就会增加一个watch对象
1 | watch = { |
$watch
函数会收集所有的watch对象到$$watchList
数组中,然后等待$digest
遍历$$watchList
中的listener,用代码表示更为直观
1 | $scope.prototype.$watch = function(name,getNewValue,listener){ |
挂载到$scope
原型上是为了每个Scope实例上存储这些函数。
$digest
$digest
函数的作用就是在触发脏检查的时候,遍历之前push到$$watchList
中的watch.listener
,用代码解释就是这样:
1 | $scope.prototype.$digest = function(){ |
watch.last
用于存储上一次的值,当newValue!=oldValue
时,就认为数据上“脏”的,就会递归调用$digest
,保证所有数据都一致。$digest
至少会调用两次,才能确保数据是干净的,当然也不会无休止的递归下去,递归10次就会抛出异常了,这种情况说明业务数据过于复杂(或者代码特别渣渣),需要开发人员优化一下了。
Vue的数据劫持模式
vue的双向数据绑定,采用数据劫持模式,核心是Object.defineProperty
,Vue3.0的核心要变成proxy
了。
通过 Object.defineProperty() 来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应监听回调。当把一个普通 Javascript 对象传给 Vue 实例来作为它的 data 选项时,Vue 将遍历它的属性,用 Object.defineProperty() 将它们转为 getter/setter。用户看不到 getter/setter,但是在内部它们让 Vue 追踪依赖,在属性被访问和修改时通知变化
Object.defineProperty
对于vue({data:{}})
,data对象上的所有属性,都会被Object.defineProperty
劫持,增加对应的getter
和 setter
,结合发布订阅模式,就可以对数据检测赋值,这里说明一下数据劫持部分,代码如下:
1 |
|
Object.defineProperty
缺点是无法监听数组变化,vue是单独对数组进行了常用方法的hack。
proxy
Proxy在ES2015规范中被正式发布,它在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写,我们可以这样认为,Proxy是Object.defineProperty的全方位加强版,proxy可以监听整个对象,无论对象内部的属性是数组还是新对象。
proxy 劫持的简单例子:
1 | const newObj = new Proxy(obj, { |
被劫持的对象会被添加对应的watcher,以此实现双向数据绑定。
参考文章: