Vue数据双向绑定原理 ( Vue.version = 2.6.14 )
Vue数据双向绑定原理 ( Vue.version = 2.6.14 )
分析主要代码
1. 监测Object的变化
在vue.commone.dev.js
文件中,Vue在defineReactive$$1
方法中监听对象的属性变化。
/**
* defineReactive$$1
*/
function defineReactive$$1 (
obj,
key,
val,监测
customSetter,
shallow
) {
// 创建Dep实例
const dep = new Dep();
// 获取属性默认的 getter/setter ,防止重新定义getter/setter 的时候覆盖默认行为
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) {
/**
* 1. 调用Dep.prototype.depend
* 2. 在Dep.prototype.depend中调用Watcher.prototype.addDep
* 3. Watcher.prototype.addDep中将Dep实例保存到Watcher实例的newDepIds、newDeps属性上,再将Watcher实例保存到Dep实例的subs属性上
*/
dep.depend();
}
return value;
},
set: function(newVal) {
const value = getter ? getter.call(obj) : val;
// 设置新的值
if (setter) {
setter.call(obj, newVal);
} else {
val = newVal;
}
/**
* 1. 调用Dep.prototype.notify
* 2. 在Dep.prototype.notify中遍历Dep实例上的subs属性 ( getter中保存的Watcher实例 )
* 3. 调用Watcher.prototype.update更新视图 ( 具体update中做了什么,下次单独分析 )
*/
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);
// 能够改变数组自身的7个方法
const methodsToPatch = [
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
];
// 遍历methodsToPatch并修改arrayMethods上对应名称的方法
methodsToPatch.forEach(method => {
Object.defineProperty(
arrayMethods,
method,
{
value: function() {
/**
* clone参数
*/
const args = [];
let len = arguments.length;
while (len--) {
args[len] = arguments[len];
}
// 调用原生的方法
const result = arrayProto[method].apply(this, args);
/**
* 调用 push/unshift/splice时若添加/更新 了新元素
* 则调用observe观察该元素
*/
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
。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具