vue 双向绑定实现
Vue 双向绑定的实现原理
Vue 的双向绑定主要通过数据劫持结合发布者-订阅者模式实现,核心是 Object.defineProperty(Vue 2.x)或 Proxy(Vue 3.x)。
数据劫持
Vue 2.x 使用 Object.defineProperty 劫持对象的属性,在属性被访问或修改时触发 getter 和 setter。Vue 3.x 改用 Proxy 代理整个对象,性能更好且能监听动态新增的属性。

// Vue 2.x 数据劫持示例
function defineReactive(obj, key, val) {
Object.defineProperty(obj, key, {
get() {
console.log('get:', val);
return val;
},
set(newVal) {
if (newVal === val) return;
console.log('set:', newVal);
val = newVal;
}
});
}
依赖收集
每个响应式属性都有一个 Dep(依赖管理器),用于收集依赖该属性的 Watcher(订阅者)。在 getter 中收集依赖,在 setter 中通知更新。
class Dep {
constructor() {
this.subs = [];
}
addSub(sub) {
this.subs.push(sub);
}
notify() {
this.subs.forEach(sub => sub.update());
}
}
模板编译
Vue 编译器将模板解析为渲染函数,过程中遇到指令(如 v-model)会生成对应的数据绑定代码。v-model 本质是 :value 和 @input 的语法糖。

// v-model 的等价形式
<input v-model="message">
// 等同于
<input :value="message" @input="message = $event.target.value">
实现简易双向绑定
以下是一个基于 Object.defineProperty 的极简实现:
function observe(obj) {
Object.keys(obj).forEach(key => {
let internalValue = obj[key];
const dep = new Dep();
Object.defineProperty(obj, key, {
get() {
dep.depend(); // 收集当前活跃的 Watcher
return internalValue;
},
set(newVal) {
internalValue = newVal;
dep.notify(); // 通知所有 Watcher 更新
}
});
});
}
class Dep {
constructor() {
this.subscribers = [];
}
depend() {
if (activeUpdate) {
this.subscribers.push(activeUpdate);
}
}
notify() {
this.subscribers.forEach(sub => sub());
}
}
let activeUpdate = null;
function autorun(update) {
function wrappedUpdate() {
activeUpdate = wrappedUpdate;
update();
activeUpdate = null;
}
wrappedUpdate();
}
Vue 3 的改进
Vue 3 使用 Proxy 替代 Object.defineProperty,优势包括:
- 直接监听整个对象而非属性
- 可检测属性的新增和删除
- 更好的性能表现
function reactive(obj) {
return new Proxy(obj, {
get(target, key) {
track(target, key);
return target[key];
},
set(target, key, value) {
target[key] = value;
trigger(target, key);
}
});
}






