Dep:依赖管理新思路
在Vue 3中,Dep不再是一个类,而是一种通过Map实现的依赖追踪方式,这代表了Vue响应式系统对依赖管理的新思路。与Vue 2相比,这种方法在性能和内存使用上带来了显著的优化。
1. Vue 2中的依赖管理
在Vue 2中,每个响应式属性都有一个Dep实例与之对应。
Dep是一个类,它内部维护了一个订阅者列表,这个列表是由副作用函数(如观察者、计算属性等)组成的。当一个响应式属性被读取时,当前激活的副作用函数会被添加到这个属性的Dep实例中;当属性发生变化时,Dep实例会通知所有订阅者重新计算或重新渲染。
这种方式在概念上清晰,易于理解,但每个响应式属性都需要创建一个Dep实例,在大规模应用中可能造成内存压力。
class Dep {
constructor() {
this.subs = []; // 订阅者列表
}
// 添加订阅者
addSub(sub) {
this.subs.push(sub);
}
// 通知所有订阅者
notify() {
this.subs.forEach(sub => sub.update());
}
}
2. Vue 3的优化:Dep类型的转变
Vue 3中的Dep是一个使用Map结构来追踪副作用函数和其调用次数的类型别名。这种设计减少了对象创建,利用原生结构提高了性能。
/**
* ts类型别名声明:它使用了交叉类型(&)来组合多个类型
*/
export type Dep = Map<ReactiveEffect, number> & {
cleanup: () => void
computed?: ComputedRefImpl<any>
}
解析:
2.1 Map<ReactiveEffect, number>:
Dep现在是一个Map,其中键是ReactiveEffect类型(副作用函数),值是number类型,用来追踪其调用次数,这有助于优化重复的副作用调用。
2.2 &:
这个符号在TypeScript中表示交叉类型,用来组合多个类型到一个类型中。这意味着Dep类型既具有Map<ReactiveEffect, number>的所有特性,又具有后面定义的对象类型的特性。
2.3 { cleanup: () => void; computed?: ComputedRefImpl; }:
这部分定义了一个带有两个属性的对象类型,提供了额外的管理功能:
**- cleanup: () => void:**表示一个名为cleanup的方法,这个方法没有参数并且返回void。这个方法的用途通常是进行清理操作,比如在依赖关系被移除时清理不再需要的副作用函数。
**- computed?: ComputedRefImpl:**表示一个可选的名为computed的属性,它的类型是ComputedRefImpl。ComputedRefImpl是计算属性的内部实现,这里使用any泛型表示计算属性可以返回任意类型的值。属性前的?符号表示这个属性是可选的,也就是说在Dep类型的对象中,computed属性可以不存在。
3. Vue 3的依赖管理机制
Vue 3采用Proxy和Reflect API,结合WeakMap和Map,实现了更高效的依赖管理。targetMap是全局的依赖映射表,它存储了响应式对象的每个属性与依赖关系的映射。
/**
* reactiveEffect.ts文件:
*/
// targetMap 是一个全局的 WeakMap,用来存储每个响应式对象与它的属性依赖关系的映射。
const targetMap = new WeakMap<object, KeyToDepMap>()
//对于每个响应式对象(target),targetMap 关联一个 KeyToDepMap,它是一个 Map,其中的键是属性名,值是一个Dep类型。
type KeyToDepMap = Map<any, Dep>
4. 性能考虑
减少内存使用:
避免了为每个响应式属性创建的Dep对象实例,降低内存占用。
优化垃圾回收:
使用WeakMap,未被引用的响应式对象及其依赖关系可以被自动回收,而不需要维护额外的引用,有利于垃圾回收器更高效地回收不再使用的响应式数据。
利用原生结构:
Map的操作(查找、插入和删除)性能优于自定义结构,加快了依赖追踪和更新触发。
5. 对比总结
Vue 3的依赖管理策略通过全局的targetMap和依赖类型的改进,提升了响应式系统的性能和效率,特别是在处理大规模数据和复杂应用时。这种设计还为Vue 3带来了更好的TypeScript支持和现代JavaScript特性的利用,展现了Vue团队对框架性能和开发体验的持续优化。
在深入理解了Dep类型和Vue 3的依赖管理机制之后,我们对于如何在Reactive对象的属性get和set操作中实施track和trigger有了更清晰的视角。让我们下一章中继续探索!