vue双向实现原理
Vue 双向绑定的实现原理
Vue 的双向绑定主要通过 v-model 指令实现,其核心是结合了数据劫持(Object.defineProperty 或 Proxy)和发布-订阅模式(观察者模式)。以下是具体实现机制:
数据劫持
Vue 2.x 使用 Object.defineProperty 对数据对象的属性进行劫持,Vue 3.x 改用 Proxy 实现更高效的响应式系统。当数据发生变化时,触发 setter 或 Proxy 的拦截操作,通知依赖更新。
// Vue 2.x 数据劫持示例
const data = { value: '' };
Object.defineProperty(data, 'value', {
get() {
return this._value;
},
set(newVal) {
this._value = newVal;
console.log('数据更新,触发视图渲染');
}
});
模板编译
v-model 在模板编译阶段会被解析为 :value 绑定和 @input 事件监听。例如:
<input v-model="message">
编译后等效于:
<input :value="message" @input="message = $event.target.value">
依赖收集与派发更新
- 依赖收集:在组件渲染时,会触发数据的 getter,将当前 Watcher(依赖)添加到订阅列表中。
- 派发更新:数据变化时,通过 setter 或 Proxy 触发通知,执行 Watcher 的更新函数,重新渲染视图。
实现双向绑定的关键步骤
1. 数据响应化
通过劫持数据对象的属性访问和修改,实现数据变化的监听。Vue 3.x 的 Proxy 示例:
const reactive = (target) => {
return new Proxy(target, {
get(target, key) {
track(target, key); // 依赖收集
return target[key];
},
set(target, key, value) {
target[key] = value;
trigger(target, key); // 触发更新
return true;
}
});
};
2. 指令解析与绑定
v-model 根据不同元素类型生成对应的绑定逻辑:
- 表单输入:监听
input或change事件,同步数据。 - 自定义组件:通过
model选项配置prop和event。
3. 事件监听与数据更新
在输入框输入时,触发 @input 事件回调,将新值赋给绑定的数据属性,从而触发响应式系统的更新流程。
代码示例:简易双向绑定
以下是一个简化版的实现:
class SimpleVue {
constructor(options) {
this._data = options.data;
this.observe(this._data);
this.compile(options.el);
}
observe(data) {
Object.keys(data).forEach(key => {
let value = data[key];
const dep = new Dep();
Object.defineProperty(data, key, {
get() {
Dep.target && dep.addSub(Dep.target);
return value;
},
set(newVal) {
value = newVal;
dep.notify();
}
});
});
}
compile(el) {
const element = document.querySelector(el);
Array.from(element.children).forEach(node => {
if (node.hasAttribute('v-model')) {
const key = node.getAttribute('v-model');
node.value = this._data[key];
node.addEventListener('input', () => {
this._data[key] = node.value;
});
}
});
}
}
class Dep {
constructor() {
this.subs = [];
}
addSub(sub) {
this.subs.push(sub);
}
notify() {
this.subs.forEach(sub => sub.update());
}
}
总结
Vue 的双向绑定本质是语法糖,通过响应式系统监听数据变化,结合模板编译将表单输入与数据属性自动同步。其核心依赖:
- 数据劫持(
Object.defineProperty/Proxy)。 - 发布-订阅模式(依赖收集与触发)。
- 指令解析(
v-model转换为:value+@input)。







