vue 双向绑定实现
Vue 双向绑定的实现原理
Vue 的双向绑定是通过数据劫持结合发布者-订阅者模式实现的,核心是 Object.defineProperty(Vue 2.x)或 Proxy(Vue 3.x)。
基于 Object.defineProperty 的实现(Vue 2.x)
-
数据劫持
通过Object.defineProperty劫持对象的属性,在属性被访问或修改时触发getter和setter。function defineReactive(obj, key, val) { Object.defineProperty(obj, key, { get() { console.log(`读取 ${key}: ${val}`); return val; }, set(newVal) { if (newVal !== val) { console.log(`设置 ${key}: ${newVal}`); val = newVal; } } }); } -
依赖收集
在getter中收集依赖(Watcher),在setter中通知依赖更新。class Dep { constructor() { this.subscribers = new Set(); } depend() { if (currentWatcher) { this.subscribers.add(currentWatcher); } } notify() { this.subscribers.forEach(watcher => watcher.update()); } } let currentWatcher = null; function defineReactive(obj, key, val) { const dep = new Dep(); Object.defineProperty(obj, key, { get() { dep.depend(); // 收集依赖 return val; }, set(newVal) { if (newVal !== val) { val = newVal; dep.notify(); // 通知更新 } } }); } -
Watcher 实现
Watcher 负责订阅数据变化并执行回调(如更新视图)。
class Watcher { constructor(fn) { this.fn = fn; this.update(); } update() { currentWatcher = this; this.fn(); currentWatcher = null; } }
基于 Proxy 的实现(Vue 3.x)
Vue 3.x 使用 Proxy 替代 Object.defineProperty,解决了深层对象监听和数组监听的问题。
function reactive(obj) {
return new Proxy(obj, {
get(target, key) {
track(target, key); // 依赖收集
return target[key];
},
set(target, key, value) {
target[key] = value;
trigger(target, key); // 触发更新
return true;
}
});
}
function track(target, key) {
// 收集依赖逻辑
}
function trigger(target, key) {
// 触发更新逻辑
}
双向绑定的完整流程
-
模板编译
Vue 模板编译时解析指令(如v-model),生成渲染函数和更新函数。 -
数据响应式
通过数据劫持或 Proxy 监听数据变化,建立依赖关系。
-
依赖更新
数据变化时触发setter或Proxy.set,通知 Watcher 更新视图。 -
视图更新
Watcher 调用渲染函数更新 DOM,完成双向绑定。
示例代码
以下是一个简化的双向绑定实现:
// 数据劫持
function defineReactive(obj, key, val) {
const dep = new Dep();
Object.defineProperty(obj, key, {
get() {
dep.depend();
return val;
},
set(newVal) {
if (newVal !== val) {
val = newVal;
dep.notify();
}
}
});
}
// 依赖管理
class Dep {
constructor() {
this.subscribers = new Set();
}
depend() {
if (currentWatcher) {
this.subscribers.add(currentWatcher);
}
}
notify() {
this.subscribers.forEach(watcher => watcher.update());
}
}
// Watcher
class Watcher {
constructor(fn) {
this.fn = fn;
this.update();
}
update() {
currentWatcher = this;
this.fn();
currentWatcher = null;
}
}
// 测试
const data = { text: 'Hello' };
defineReactive(data, 'text', data.text);
new Watcher(() => {
console.log('视图更新:', data.text);
});
data.text = 'World'; // 输出 "视图更新: World"
总结
- Vue 2.x 使用
Object.defineProperty实现数据劫持,但无法监听数组和深层对象。 - Vue 3.x 使用
Proxy解决上述问题,性能更好。 - 双向绑定的核心是依赖收集和发布-订阅模式。






