vue 响应式实现
Vue 响应式原理
Vue 的响应式系统通过数据劫持结合发布-订阅模式实现。核心是利用 Object.defineProperty(Vue 2.x)或 Proxy(Vue 3.x)监听数据变化,触发依赖更新。
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) return;
console.log(`更新 ${key}`);
val = newVal;
}
});
}
依赖收集
每个属性对应一个 Dep 实例,用于存储依赖(Watcher)。在 getter 中收集依赖,setter 中通知更新:
class Dep {
constructor() {
this.subs = [];
}
addSub(sub) {
this.subs.push(sub);
}
notify() {
this.subs.forEach(sub => sub.update());
}
}
function defineReactive(obj, key, val) {
const dep = new Dep();
Object.defineProperty(obj, key, {
get() {
if (Dep.target) dep.addSub(Dep.target);
return val;
},
set(newVal) {
val = newVal;
dep.notify();
}
});
}
Vue 3.x 实现方式
Proxy 代理
Vue 3 使用 Proxy 监听整个对象,无需递归遍历:
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); // 触发更新
return true;
}
});
}
依赖管理
通过 WeakMap 和 Map 建立响应式对象与依赖的映射关系:
const targetMap = new WeakMap();
function track(target, key) {
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直接代理整个对象,无需递归初始化。Object.defineProperty需要遍历每个属性,对嵌套对象需递归处理。
-
数组监听
Proxy可监听数组索引变化和push/pop等方法。Object.defineProperty需重写数组方法实现响应式。
-
新数据类型支持

Proxy支持Map、Set等数据结构。Object.defineProperty仅适用于纯对象。
注意事项
- 避免直接替换响应式对象:重新赋值会丢失响应性,应使用
Vue.set或更新嵌套属性。 - 异步更新队列:Vue 将 DOM 更新合并为异步任务,减少重复渲染。






