Vue3 的响应式系统源码级 WeakMap 与依赖存储结构设计原理
字数 846 2025-11-26 21:53:12
Vue3 的响应式系统源码级 WeakMap 与依赖存储结构设计原理
题目描述
Vue3 的响应式系统通过 Proxy 拦截对象操作,但依赖收集的具体存储结构设计是关键。系统使用 WeakMap、Map 和 Set 构成三级嵌套结构来精确管理依赖关系。这种结构如何实现高效的依赖关联与内存管理?
知识要点
- 依赖存储的三级结构:WeakMap → Map → Set
- WeakMap 的键是目标对象(target),值是 Map
- Map 的键是属性(key),值是 Set 集合(存储副作用函数)
- WeakMap 的弱引用特性如何避免内存泄漏
实现原理分步解析
1. 核心存储结构定义
在 Vue3 源码中,依赖存储结构如下:
// 三级依赖存储结构
targetMap: WeakMap<Target, Map<string | symbol, Set<ReactiveEffect>>>
- 第一级:WeakMap,键是响应式对象(target),值是第二级的 Map
- 第二级:Map,键是对象的属性名(key),值是第三级的 Set
- 第三级:Set,存储该属性对应的所有副作用函数(effect)
2. 结构初始化流程
当首次访问响应式对象属性时,系统会逐步构建这个结构:
// 简化版源码逻辑
function track(target, key) {
// 1. 从 targetMap 中查找 target 对应的 depsMap
let depsMap = targetMap.get(target)
if (!depsMap) {
// 首次访问该target:建立 WeakMap 关联
targetMap.set(target, (depsMap = new Map()))
}
// 2. 从 depsMap 中查找 key 对应的 dep Set
let dep = depsMap.get(key)
if (!dep) {
// 首次访问该属性:建立 Map 关联
depsMap.set(key, (dep = new Set()))
}
// 3. 将当前激活的 effect 添加到 dep Set
dep.add(activeEffect)
}
3. WeakMap 的内存管理优势
- 弱引用特性:WeakMap 的键是弱引用,当 target 对象没有其他引用时,会被垃圾回收
- 自动清理:target 被回收后,对应的 depsMap 也会自动从 WeakMap 中移除
- 避免内存泄漏:相比 Map,WeakMap 不会阻止键对象被回收,特别适合存储元数据
4. 依赖触发流程
当属性值变化时,通过相同结构快速定位所有需要触发的 effect:
function trigger(target, key) {
// 1. 通过 target 获取 depsMap
const depsMap = targetMap.get(target)
if (!depsMap) return
// 2. 通过 key 获取 dep Set
const dep = depsMap.get(key)
if (dep) {
// 3. 遍历执行所有关联的 effect
dep.forEach(effect => {
if (effect.scheduler) {
effect.scheduler()
} else {
effect()
}
})
}
}
5. 结构设计的关键优势
- 精确更新:通过属性级依赖追踪,避免不必要的重渲染
- 高效查找:Map 和 Set 的查找时间复杂度为 O(1)
- 内存高效:WeakMap 自动清理无引用的依赖关系
- 类型安全:使用 Symbol 作为 key 时也能正确工作
实际应用示例
const obj = reactive({ a: 1, b: 2 })
// 触发依赖收集
effect(() => {
console.log(obj.a) // 只收集 a 的依赖
})
// 结构状态:
// targetMap: WeakMap { obj → depsMap }
// depsMap: Map { 'a' → Set[effect] } // 不包含 b 的依赖
obj.b = 3 // 不会触发 effect,因为 effect 不依赖 b
这种三级存储结构是 Vue3 响应式系统高效性的基石,确保了依赖关系的精确管理和内存使用的优化。