vue实现双向数据绑定的原理
Vue 双向数据绑定的原理
Vue 的双向数据绑定主要通过 数据劫持 和 发布-订阅模式 实现,核心是 Object.defineProperty(Vue 2.x)或 Proxy(Vue 3.x)。以下是具体实现机制:
数据劫持(响应式系统)
Vue 通过劫持对象的属性,在属性被访问或修改时触发更新。
Vue 2.x 使用 Object.defineProperty:
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;
// 触发视图更新
}
}
});
}
Vue 3.x 使用 Proxy:
const reactive = (target) => {
return new Proxy(target, {
get(target, key, receiver) {
console.log(`读取 ${key}`);
return Reflect.get(target, key, receiver);
},
set(target, key, value, receiver) {
console.log(`设置 ${key}: ${value}`);
Reflect.set(target, key, value, receiver);
// 触发视图更新
return true;
}
});
};
依赖收集与发布-订阅
Vue 通过 Dep(依赖收集器) 和 Watcher(观察者) 实现数据变化时的视图更新。
-
Dep(依赖收集器)
每个响应式属性对应一个Dep实例,用于存储依赖该属性的Watcher。class Dep { constructor() { this.subscribers = []; } depend() { if (target && !this.subscribers.includes(target)) { this.subscribers.push(target); } } notify() { this.subscribers.forEach(sub => sub()); } } -
Watcher(观察者)
在模板编译时,为每个需要更新的 DOM 节点创建Watcher,订阅数据变化。let target = null; function watcher(fn) { target = fn; fn(); // 首次执行触发 getter,收集依赖 target = null; }
双向绑定的实现(v-model)
v-model 是语法糖,本质是 :value 绑定和 @input 监听的组合:
<input v-model="message">
<!-- 等价于 -->
<input :value="message" @input="message = $event.target.value">
-
初始化阶段
- 通过数据劫持监听
message的变化。 - 编译模板时,为
input创建Watcher,订阅message的Dep。
- 通过数据劫持监听
-
数据更新流程
- 用户输入触发
@input,修改message。 message的setter调用Dep.notify(),通知所有Watcher更新视图。
- 用户输入触发
Vue 3.x 的优化
Vue 3 使用 Proxy 替代 Object.defineProperty,解决了以下问题:
- 无需递归遍历对象属性,性能更高。
- 直接监听数组变化,无需重写数组方法。
- 支持动态新增/删除属性。
总结
- 数据劫持:通过
Object.defineProperty或Proxy监听数据变化。 - 依赖收集:
Dep和Watcher实现数据与视图的关联。 - 双向绑定:
v-model结合:value和@input实现表单控件与数据的同步。







