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}`);
return val;
},
set(newVal) {
if (newVal !== val) {
console.log(`更新 ${key}`);
val = newVal;
}
}
});
}
Vue 3.x 使用 Proxy:
直接代理整个对象,无需遍历属性,性能更高且支持嵌套对象和数组。
const observed = new Proxy(obj, {
get(target, key) {
console.log(`读取 ${key}`);
return target[key];
},
set(target, key, value) {
console.log(`更新 ${key}`);
target[key] = value;
return true;
}
});
依赖收集(Dependency Tracking)
Watcher(订阅者):
每个组件实例对应一个 Watcher,在渲染过程中会触发数据的 getter,将当前 Watcher 添加到依赖列表中。
Dep(发布者):
每个属性有一个 Dep 实例,用于存储所有依赖该属性的 Watcher。当数据变化时,通过 setter 通知 Dep,触发所有 Watcher 更新。
class Dep {
constructor() {
this.subscribers = [];
}
depend() {
if (target && !this.subscribers.includes(target)) {
this.subscribers.push(target);
}
}
notify() {
this.subscribers.forEach(sub => sub());
}
}
模板编译(Template Compilation)
Vue 的模板会被编译为渲染函数,解析指令(如 v-model)时:
- 对表单元素绑定
input事件,将用户输入同步到数据。 - 通过数据劫持将数据变化更新到视图。
<input v-model="message">
编译后的逻辑类似:
input.addEventListener('input', (e) => {
data.message = e.target.value;
});
更新视图(View Update)
数据变化时,触发 setter 通知 Dep,Dep 调用所有 Watcher 的更新方法,重新渲染虚拟 DOM 并 patch 到真实 DOM。
对比 Vue 2.x 与 3.x
-
Vue 2.x:
- 基于
Object.defineProperty,需递归遍历对象。 - 无法检测数组索引变化和对象新增属性(需
Vue.set)。
- 基于
-
Vue 3.x:
- 基于
Proxy,支持动态新增属性和数组索引变化。 - 性能优化,减少不必要的依赖追踪。
- 基于
通过以上机制,Vue 实现了数据变化自动更新视图、视图输入自动更新数据的双向绑定效果。






