Vue 的依赖收集是其响应式系统的核心机制之一,它通过依赖追踪来实现数据的自动更新。
具体来说,Vue 在初始化时会为每个响应式属性创建一个 Dep(依赖)对象,并在属性被访问时收集依赖(即 Watcher),在属性被修改时通知依赖更新。
以下是 Vue 收集依赖的详细过程:
1. 响应式数据的初始化
Vue 通过 Object.defineProperty
(Vue 2)或 Proxy
(Vue 3)将数据对象转换为响应式对象。
Vue 2 的实现
在 Vue 2 中,Vue 会遍历数据对象的每个属性,使用 Object.defineProperty
为每个属性定义 getter
和 setter
。
示例:
function defineReactive(obj, key, val) {
const dep = new Dep(); // 每个属性对应一个 Dep 对象
Object.defineProperty(obj, key, {
get() {
if (Dep.target) {
dep.depend(); // 收集依赖
}
return val;
},
set(newVal) {
if (newVal === val) return;
val = newVal;
dep.notify(); // 通知依赖更新
},
});
}
说明:
- 每个属性都有一个对应的
Dep
对象,用于存储依赖(即 Watcher)。 - 在
getter
中调用dep.depend()
收集依赖。 - 在
setter
中调用dep.notify()
通知依赖更新。
Vue 3 的实现
在 Vue 3 中,Vue 使用 Proxy
实现响应式数据。
示例:
function reactive(obj) {
return new Proxy(obj, {
get(target, key, receiver) {
const dep = getDep(target, key); // 获取或创建 Dep 对象
if (Dep.target) {
dep.depend(); // 收集依赖
}
return Reflect.get(target, key, receiver);
},
set(target, key, value, receiver) {
const dep = getDep(target, key); // 获取或创建 Dep 对象
const result = Reflect.set(target, key, value, receiver);
dep.notify(); // 通知依赖更新
return result;
},
});
}
说明:
- 使用
Proxy
拦截对象的get
和set
操作。 - 在
get
中收集依赖,在set
中通知依赖更新。
2. 依赖的收集
依赖的收集是通过 Watcher 实现的。Watcher 是一个观察者,它会在组件渲染或计算属性求值时被创建。
Watcher 的创建
- 在组件渲染时,Vue 会创建一个 Watcher 来观察模板中用到的响应式数据。
- 在计算属性求值时,Vue 也会创建一个 Watcher 来观察计算属性依赖的响应式数据。
示例:
class Watcher {
constructor(vm, expOrFn, cb) {
this.vm = vm;
this.getter = parsePath(expOrFn); // 解析表达式或函数
this.cb = cb;
this.value = this.get(); // 初始化时触发依赖收集
}
get() {
Dep.target = this; // 将当前 Watcher 设置为全局的 Dep.target
const value = this.getter.call(this.vm, this.vm); // 访问响应式数据,触发 getter
Dep.target = null; // 重置 Dep.target
return value;
}
update() {
const oldValue = this.value;
this.value = this.get(); // 重新获取值
this.cb.call(this.vm, this.value, oldValue); // 执行回调
}
}
说明:
- 在 Watcher 初始化时,会调用
get()
方法,访问响应式数据,触发getter
。 - 在
getter
中,Vue 会将当前 Watcher 添加到 Dep 中,完成依赖收集。
3. 依赖的更新
当响应式数据发生变化时,Vue 会通知所有依赖该数据的 Watcher 进行更新。
示例:
class Dep {
constructor() {
this.subs = []; // 存储 Watcher
}
depend() {
if (Dep.target) {
this.subs.push(Dep.target); // 收集 Watcher
}
}
notify() {
this.subs.forEach(watcher => watcher.update()); // 通知 Watcher 更新
}
}
说明:
- 在
setter
中,Vue 会调用dep.notify()
,通知所有 Watcher 更新。 - Watcher 的
update()
方法会重新求值并执行回调。
4. 依赖收集的流程
- 初始化阶段:
- Vue 将数据对象转换为响应式对象,为每个属性创建 Dep 对象。
- 在组件渲染或计算属性求值时,创建 Watcher。
- 依赖收集阶段:
- Watcher 访问响应式数据,触发
getter
。 - 在
getter
中,Vue 将当前 Watcher 添加到 Dep 中。
- Watcher 访问响应式数据,触发
- 依赖更新阶段:
- 响应式数据发生变化时,触发
setter
。 - 在
setter
中,Vue 调用dep.notify()
,通知所有 Watcher 更新。
- 响应式数据发生变化时,触发
5. 总结
- 依赖收集的核心:通过
getter
收集依赖,通过setter
通知依赖更新。 - Dep:每个响应式属性对应一个 Dep 对象,用于存储 Watcher。
- Watcher:观察者,负责观察响应式数据并在数据变化时执行更新。
- 流程:
- 初始化响应式数据。
- 在访问数据时收集依赖。
- 在数据变化时通知依赖更新。
通过这种机制,Vue 实现了数据的自动更新,确保视图与数据保持同步。
THE END
暂无评论内容