vue 观察者实现
Vue 观察者实现原理
Vue 的响应式系统核心基于观察者模式,通过 Object.defineProperty(Vue 2)或 Proxy(Vue 3)实现数据劫持,结合依赖收集和派发更新机制实现数据变化时的自动视图更新。
Vue 2 实现方式
数据劫持
通过 Object.defineProperty 递归遍历对象属性,将属性转为 getter/setter:
function defineReactive(obj, key, val) {
Object.defineProperty(obj, key, {
get() {
console.log('读取属性', key);
return val;
},
set(newVal) {
if (newVal !== val) {
console.log('设置属性', key);
val = newVal;
}
}
});
}
依赖收集
每个被观察的数据属性关联一个 Dep 实例(依赖管理器),在 getter 中收集依赖(Watcher),在 setter 中通知更新:
class Dep {
constructor() {
this.subs = []; // 存储 Watcher 实例
}
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) { // 当前 Watcher 实例
dep.addSub(Dep.target);
}
return val;
},
set(newVal) {
if (newVal !== val) {
val = newVal;
dep.notify(); // 触发更新
}
}
});
}
Watcher 实现 Watcher 作为观察者,在初始化时触发 getter 以收集依赖:
class Watcher {
constructor(vm, exp, cb) {
this.vm = vm;
this.exp = exp;
this.cb = cb;
Dep.target = this; // 标记当前 Watcher
this.value = this.get(); // 触发 getter 收集依赖
Dep.target = null;
}
get() {
return this.vm[this.exp]; // 触发数据属性的 getter
}
update() {
const newValue = this.get();
if (newValue !== this.value) {
this.cb(newValue);
this.value = newValue;
}
}
}
Vue 3 实现方式
Vue 3 改用 Proxy 实现数据劫持,解决 Vue 2 中无法检测新增属性/数组变化的问题:

Proxy 代理
function reactive(obj) {
return new Proxy(obj, {
get(target, key, receiver) {
track(target, key); // 依赖收集
return Reflect.get(target, key, receiver);
},
set(target, key, value, receiver) {
Reflect.set(target, key, value, receiver);
trigger(target, key); // 触发更新
return true;
}
});
}
依赖管理
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());
}
Effect 函数
替代 Vue 2 的 Watcher,通过 effect 函数自动执行依赖收集:

let activeEffect = null;
function effect(fn) {
activeEffect = fn;
fn(); // 执行时触发 getter 收集依赖
activeEffect = null;
}
关键差异
-
劫持方式
- Vue 2 使用
Object.defineProperty,需递归遍历对象且无法检测新增属性。 - Vue 3 使用
Proxy,支持动态属性添加和数组索引修改。
- Vue 2 使用
-
性能优化
- Vue 3 的
Proxy直接代理整个对象,无需递归初始化所有属性。
- Vue 3 的
-
依赖追踪
- Vue 2 通过 Dep/Watcher 层级结构管理依赖。
- Vue 3 通过
WeakMap/Map/Set实现更细粒度的依赖关系。






