Vue数据双向绑定原理 ( Vue.version = 2.6.14 )
分析主要代码
1. 监测Object的变化
在vue.commone.dev.js 文件中,Vue在defineReactive$$1 方法中监听对象的属性变化。
function defineReactive$$1 (
obj,
key,
val,监测
customSetter,
shallow
) {
const dep = new Dep();
const descriptor = Object.getOwnPropertyDescriptor(obj, key);
const getter = descriptor && descriptor.get;
const setter = descriptor && descriptor.set;
if ((!getter || setter) && arguments.length === 2) {
val = obj[key];
}
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function() {
const value = getter ? getter.call(obj) : val;
if (Dep.target) {
dep.depend();
}
return value;
},
set: function(newVal) {
const value = getter ? getter.call(obj) : val;
if (setter) {
setter.call(obj, newVal);
} else {
val = newVal;
}
dep.notify();
}
})
}
整体流程:
① 通过observer ,使用Object.defineProperty 让data 的获取、变化能够被监测
② 获取data 时,触发getter , 将依赖 添加到watcher 中,将wacher 添加到依赖 的subs ( 订阅? ) 中
③ 更新data 时,触发setter ,通知依赖 遍历subs ,并调用watcher 的update 方法更新视图 ( 其实最终调用的是Vue实例 的_render 方法 )
疑问:
① watcher 是在何时创建的?
在initComputed 阶段创建watcher ,并且watcher的vm属性 指向Vue实例 ,将watcher 保存到Vue实例的_watchers 中
不足:
- 向对象中添加或删除属性时,无法监测到变化
- 无法监测数组的变化
2. 监测Array的变化
定义拦截器 ,在调用修改 数组的方法时,触发拦截器。
const arrayProto = Array.prototype;
const arrayMethods = Object.create(arrayProto);
const methodsToPatch = [
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
];
methodsToPatch.forEach(method => {
Object.defineProperty(
arrayMethods,
method,
{
value: function() {
const args = [];
let len = arguments.length;
while (len--) {
args[len] = arguments[len];
}
const result = arrayProto[method].apply(this, args);
const ob = this.__ob__;
let inserted;
switch (method) {
case 'push':
case 'unshift':
inserted = args;
break;
case 'splice':
inserted = args.slice(2);
break;
default:
break;
}
if (inserted) {
ob.observeArray(inserted);
}
ob.dep.notify();
return result;
}
}
)
})
整体流程:
① 给Array.prototype 上能改变数组本身的方法 添加拦截器
② 在Observer的构造器中 将Array实例的__proto__ 指向arrayMethods
③ 调用这些方法时,通知依赖更新
不足:
3. Vue对以上不足的解决方案
增加了两个全局API:Vue.set 和Vue.delete 。
|