vue依赖收集的实现
Vue 依赖收集的实现原理
Vue 的依赖收集是通过响应式系统中的 Observer、Dep 和 Watcher 三个核心类协作完成的。以下是具体实现机制:
数据劫持(Observer)
Vue 通过 Object.defineProperty 或 Proxy(Vue 3)劫持数据的访问和修改。在 Observer 类中,递归遍历对象的属性,将其转化为响应式数据:
class Observer {
constructor(value) {
this.value = value;
this.walk(value);
}
walk(obj) {
Object.keys(obj).forEach(key => {
defineReactive(obj, key, obj[key]);
});
}
}
defineReactive 函数为每个属性创建一个 Dep 实例(依赖管理器),并在 getter 和 setter 中触发依赖收集和更新。
依赖管理(Dep)
Dep 类负责管理依赖(即 Watcher 实例),核心逻辑如下:
class Dep {
constructor() {
this.subs = []; // 存储 Watcher 实例
}
addSub(sub) {
this.subs.push(sub);
}
depend() {
if (Dep.target) { // Dep.target 是当前正在计算的 Watcher
Dep.target.addDep(this);
}
}
notify() {
this.subs.forEach(sub => sub.update());
}
}
- 依赖收集:在
getter中调用dep.depend(),将当前Watcher添加到subs中。 - 触发更新:在
setter中调用dep.notify(),通知所有Watcher执行更新。
观察者(Watcher)
Watcher 是依赖的消费者,在组件渲染、计算属性或侦听器中被创建:
class Watcher {
constructor(vm, expOrFn, cb) {
this.vm = vm;
this.getter = expOrFn; // 更新函数(如渲染函数)
this.cb = cb;
this.value = this.get();
}
get() {
Dep.target = this; // 设置全局目标 Watcher
const value = this.getter.call(this.vm); // 触发 getter,收集依赖
Dep.target = null; // 重置
return value;
}
update() {
this.run();
}
run() {
const value = this.get();
if (value !== this.value) {
this.cb.call(this.vm, value, this.value);
}
}
}
- 依赖收集过程:
Watcher在初始化时执行get()方法,触发数据的getter,将自身(Dep.target)添加到依赖列表中。 - 更新过程:数据变化时,
Dep通知所有Watcher执行update(),重新计算或渲染。
依赖收集的触发场景
- 组件渲染:渲染函数执行时,访问响应式数据会触发
getter,将渲染Watcher作为依赖收集。 - 计算属性:计算属性的
Watcher在计算时收集其依赖的数据。 - 侦听器:
watch选项或$watch创建的Watcher会监听特定数据的变化。
关键点总结
- 全局唯一 Watcher:通过
Dep.target标记当前正在计算的Watcher,确保依赖关系准确。 - 避免重复收集:
Watcher会记录已订阅的Dep,防止重复添加。 - 性能优化:Vue 3 使用
Proxy和WeakMap改进依赖收集效率,支持动态属性增删。
通过这种机制,Vue 实现了数据变化到视图更新的自动关联。







