vue响应式依赖收集不是树结构,而是weakmap→map→set三层映射:weakmap以响应式对象为键存depsmap,map以属性名为键存依赖effect的set,effect自身deps集用于清理旧依赖,确保精准通知。

Vue 响应式数据追踪里的“依赖收集树”,其实并不是一棵真正意义上的树结构,而是一种以对象为根、属性为分支、effect 为叶子的三层映射关系。它不靠父子层级维系,而是靠 WeakMap → Map → Set 的嵌套结构,动态记录“哪个对象的哪个属性被哪些副作用函数读取过”。理解它,关键不是画树,而是看清这三层怎么联动。
WeakMap 是对象级入口:谁被响应式了,就记谁
每个经过 reactive() 处理的对象(比如 { count: 0, list: [] }),都会作为键存进全局的 reactiveMap: WeakMap 中。它的值不是数据本身,而是另一个用于管理该对象内部所有属性依赖的 Map(常叫 depsMap)。
选 WeakMap 不是为了炫技,而是务实:
- 键只能是对象——天然匹配“目标响应式对象”这个角色
- 弱引用机制——当原始对象在其他地方不再被持有,垃圾回收器就能顺带清掉它在 WeakMap 里的整条记录,避免内存泄漏
Map 是属性级索引:按 key 分门别类存依赖
depsMap 就是 WeakMap 中每个对象对应的“属性账本”。它的键是字符串属性名(如 'count'、'list'),值是一个 Set,里面装着所有读取过该属性的副作用函数(effect)。
比如执行:effect(() => console.log(state.count))
→ 读取 state.count 触发 track()
→ 找到 reactiveMap.get(state) 对应的 depsMap
→ 再从 depsMap.get('count') 拿出那个 Set,把当前 effect 加进去
effect 自带 deps 集合:方便清理“旧账”
每个 effect 函数自身还维护一个 deps: Set,里面存的是它自己依赖的所有 dep(即那些属性对应的 Set)。这个设计解决了一个关键问题:重复执行时的依赖残留。
分析 Vue 3 Composition API 使用情况——审计响应式模式、可组合函数设计、ref 与 reactive 的选择、计算属性、watcher 使用及组件……
每次 effect 重新运行前,会先调用 cleanup():
- 遍历自己的
deps集合 - 从每个 dep(比如
count对应的 Set)里把自己删掉 - 再重新走一遍 track,建立最新依赖关系
这样就保证了依赖关系始终干净、准确——不会因为上一次渲染遗留的 watcher,导致这次不该触发时还被通知。
没有显式树,但有清晰路径:查起来很直接
整个结构像一张三层索引表,数据变化时顺着查就行:
- 第一层:用对象查 WeakMap → 找到它的
depsMap - 第二层:用属性名查
depsMap→ 找到该属性的依赖集合(Set) - 第三层:遍历那个
Set→ 逐个触发里面的effect
它不递归嵌套、不维护 parent-child 关系,却能做到精准通知——改了 state.user.name,只通知用过 name 的 effect,不影响 state.user.age 或 state.list 的监听者。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!









