vue的双向绑定实现
vue2 的双向绑定实现
Vue2 使用 Object.defineProperty 实现数据劫持,结合发布-订阅模式完成响应式更新。
数据劫持
通过 Object.defineProperty 递归地将数据对象的属性转换为 getter/setter:
function defineReactive(obj, key, val) {
Object.defineProperty(obj, key, {
get() {
console.log(`读取 ${key}`);
return val;
},
set(newVal) {
if (newVal === val) return;
console.log(`设置 ${key}`);
val = newVal;
}
});
}
依赖收集 每个属性对应一个 Dep 实例,用于存储依赖(Watcher):

class Dep {
constructor() {
this.subs = [];
}
addSub(sub) {
this.subs.push(sub);
}
notify() {
this.subs.forEach(sub => sub.update());
}
}
观察者模式 Watcher 在初始化时触发 getter 完成依赖收集:
class Watcher {
constructor(vm, key, cb) {
this.vm = vm;
this.key = key;
this.cb = cb;
Dep.target = this;
this.vm[this.key]; // 触发getter
Dep.target = null;
}
update() {
this.cb.call(this.vm, this.vm[this.key]);
}
}
vue3 的双向绑定实现
Vue3 改用 Proxy 实现响应式系统,解决了 Vue2 的数组监听和新增属性问题。

Proxy 代理
function reactive(obj) {
return new Proxy(obj, {
get(target, key) {
track(target, key);
return Reflect.get(target, key);
},
set(target, key, value) {
Reflect.set(target, key, value);
trigger(target, key);
}
});
}
依赖跟踪 使用 WeakMap 建立目标对象与依赖的映射关系:
const targetMap = new WeakMap();
function track(target, key) {
if (!activeEffect) return;
let depsMap = targetMap.get(target);
if (!depsMap) {
targetMap.set(target, (depsMap = new Map()));
}
let dep = depsMap.get(key);
if (!dep) {
depsMap.set(key, (dep = new Set()));
}
dep.add(activeEffect);
}
触发更新
function trigger(target, key) {
const depsMap = targetMap.get(target);
if (!depsMap) return;
const effects = depsMap.get(key);
effects && effects.forEach(effect => effect());
}
对比差异
- 性能优化:Proxy 直接代理整个对象,不需要递归遍历属性
- 数组处理:Proxy 能监听到数组索引变化和 length 修改
- 新增属性:Proxy 自动响应新增属性,无需
$set - 内存占用:Proxy 使用 WeakMap 存储依赖,对象被回收时自动释放
实现双向绑定的关键点
- 数据劫持:通过拦截对象属性的读写操作
- 依赖收集:建立数据与视图的对应关系
- 派发更新:数据变化时通知相关依赖更新
- 批量更新:使用异步队列避免重复渲染
以上实现展示了 Vue 响应式系统的核心原理,实际实现还包含虚拟 DOM diff、组件生命周期等复杂逻辑。






