vue 双向绑定 实现原理
Vue 双向绑定的实现原理
Vue 的双向绑定是通过数据劫持结合发布者-订阅者模式实现的,核心是 Object.defineProperty(Vue 2.x)或 Proxy(Vue 3.x)。以下是具体实现机制:
数据劫持(Data Observation)
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) return;
console.log(`设置 ${key}: ${newVal}`);
val = newVal;
// 触发更新
},
});
}
Vue 3.x 改用 Proxy,可以直接监听对象而非属性,且能拦截更多操作(如数组方法)。
// Vue 3.x 使用 Proxy
const observed = new Proxy(data, {
get(target, key) {
track(target, key); // 依赖收集
return target[key];
},
set(target, key, value) {
target[key] = value;
trigger(target, key); // 触发更新
},
});
依赖收集(Dependency Tracking)
通过 Dep 类(依赖管理器)和 Watcher 类(订阅者)实现:
- 每个响应式属性有一个
Dep实例,用于存储依赖它的Watcher。 - 在
getter中调用dep.depend()收集当前Watcher。 - 在
setter中调用dep.notify()通知所有Watcher更新。
class Dep {
constructor() {
this.subscribers = [];
}
depend() {
if (targetWatcher) {
this.subscribers.push(targetWatcher);
}
}
notify() {
this.subscribers.forEach(sub => sub.update());
}
}
模板编译(Template Compilation)
Vue 的模板会被编译成渲染函数(render),过程中解析指令(如 v-model):
v-model本质是v-bind+v-on的语法糖。- 对于输入框,
v-model会动态绑定value并监听input事件。
<input v-model="message">
<!-- 等价于 -->
<input :value="message" @input="message = $event.target.value">
双向绑定的实现流程
- 初始化阶段:通过数据劫持监听所有响应式属性。
- 编译阶段:解析模板,为每个绑定创建
Watcher,触发getter完成依赖收集。 - 更新阶段:数据变化时触发
setter,通知Watcher更新视图。
示例代码(简化版)
// 数据劫持 + 依赖收集
let targetWatcher = null;
class Watcher {
constructor(updateFn) {
this.updateFn = updateFn;
targetWatcher = this;
updateFn(); // 触发 getter
targetWatcher = null;
}
update() {
this.updateFn();
}
}
function observe(data) {
for (let key in data) {
let val = data[key];
const dep = new Dep();
Object.defineProperty(data, key, {
get() {
dep.depend();
return val;
},
set(newVal) {
val = newVal;
dep.notify();
},
});
}
}
// 使用
const data = { message: "Hello" };
observe(data);
new Watcher(() => {
console.log(`DOM 更新: ${data.message}`);
});
data.message = "World"; // 触发更新
总结
- Vue 2.x:基于
Object.defineProperty,需递归遍历对象,对数组方法重写。 - Vue 3.x:基于
Proxy,支持动态新增属性,性能更优。 - 核心思想:数据劫持 + 依赖收集 + 事件监听。







