vue双向绑定的实现
Vue 双向绑定的实现原理
Vue 的双向绑定主要通过数据劫持结合发布-订阅模式实现,核心是 Object.defineProperty(Vue 2.x)或 Proxy(Vue 3.x)。以下是具体实现方式:
数据劫持(响应式原理)
在 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;
// 触发更新(模拟发布-订阅)
dep.notify();
}
},
});
}
在 Vue 3.x 中,改用 Proxy 实现更高效的劫持:
function reactive(obj) {
return new Proxy(obj, {
get(target, key) {
console.log(`读取 ${key}`);
return Reflect.get(target, key);
},
set(target, key, value) {
if (target[key] !== value) {
console.log(`设置 ${key}: ${value}`);
Reflect.set(target, key, value);
// 触发更新
trigger(target, key);
}
return true;
},
});
}
依赖收集与发布-订阅
-
依赖收集:在 getter 中收集依赖(Watcher),建立数据与视图的关联。
class Dep { constructor() { this.subscribers = []; } depend() { if (targetWatcher) { this.subscribers.push(targetWatcher); } } notify() { this.subscribers.forEach(sub => sub.update()); } } -
Watcher:监听数据变化并更新视图。
class Watcher { constructor(vm, key, updateFn) { this.vm = vm; this.key = key; this.updateFn = updateFn; targetWatcher = this; // 全局标记当前 Watcher this.vm[this.key]; // 触发 getter 收集依赖 targetWatcher = null; } update() { this.updateFn.call(this.vm, this.vm[this.key]); } }
指令解析与绑定
Vue 的模板编译器会将模板中的指令(如 v-model)解析为对应的数据绑定和事件监听:
<input v-model="message">
编译后等效于:
<input
:value="message"
@input="message = $event.target.value"
>
实现双向绑定的关键步骤
- 数据劫持:拦截数据的读写操作。
- 依赖收集:在 getter 中记录依赖关系。
- 派发更新:在 setter 中通知所有依赖更新。
- 指令绑定:将模板指令转换为数据绑定和事件监听。
代码示例(简易实现)
// 简易 Vue 双向绑定实现
class MiniVue {
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() {
if (targetWatcher) dep.depend();
return value;
},
set(newVal) {
if (newVal !== value) {
value = newVal;
dep.notify();
}
},
});
});
}
compile(el) {
const element = document.querySelector(el);
this.walkNodes(element);
}
walkNodes(node) {
if (node.nodeType === 1) { // Element
Array.from(node.attributes).forEach(attr => {
if (attr.name.startsWith('v-')) {
const dir = attr.name.substring(2);
if (dir === 'model') {
const key = attr.value;
node.value = this.$data[key];
new Watcher(this, key, val => {
node.value = val;
});
node.addEventListener('input', e => {
this.$data[key] = e.target.value;
});
}
}
});
}
node.childNodes.forEach(child => this.walkNodes(child));
}
}
总结
- Vue 2.x 使用
Object.defineProperty实现数据劫持,Vue 3.x 改用Proxy。 - 通过发布-订阅模式(Dep/Watcher)实现依赖收集和更新派发。
v-model是语法糖,本质是:value+@input的绑定组合。







