Computed 依赖清除的本质是“无人监听即卸载”:当组件卸载、computed 值未被访问或仅被已停用的 effect 使用时,Vue 会自动清理其依赖记录和缓存,依托 WeakMap 等弱引用结构实现无泄漏回收。

Computed 依赖清除的本质是“无人监听即卸载”
Computed 不会永远挂着它的依赖关系。当组件卸载、计算属性不再被任何地方读取(比如模板没用它、代码里没访问 .value、也没有其他 computed 或 effect 依赖它),Vue 就会判定它“失去所有订阅者”,进而清理它所收集的依赖记录和内部缓存。
依赖清除发生的三个典型场景
以下情况会触发依赖清除逻辑:
- 组件 unmounted:整个组件实例销毁,其内部所有 computed 的 effect 都被 stop,对应的依赖图(targetMap 中的记录)随之释放
- computed 值长期未被访问:如果一个 computed 创建后从未被读取(lazy: true 且无任何 track),它连依赖都不会开始收集,自然无须清除
- 仅被临时 effect 使用且 effect 已停止:例如在 watchEffect 内读取了某个 computed,该 watchEffect 被手动 stop 或因条件失效而停用,若此时再无其他 active 订阅者,该 computed 的依赖也会逐步被 GC 回收
底层如何实现“自动释放”?
关键在于 Vue 的响应式追踪结构是弱引用友好的:
- 依赖存储使用 WeakMap → Map → Set 三层结构:外层 WeakMap 的 key 是响应式对象(如 reactive),一旦对象被 JS 垃圾回收,整条依赖链自动断开
- 每个 computed 实例持有自己的 ReactiveEffect,effect 内部有 deps 数组记录它依赖的 Dep 实例;当 effect 停止(stop),它会主动从所有 Dep 的 subs 列表中移除自己
- 没有活跃订阅者的 Dep 实例,只要其所属的响应式对象也被回收,就不会造成内存泄漏
你不需要手动清,但要避免阻止清除的行为
以下写法可能意外延长依赖生命周期,阻碍自动清除:
- 把 computed 结果赋给全局变量或闭包外的常量(如 window.lastResult = myComputed.value),导致引用无法释放
- 在 onBeforeUnmount 中忘记 stop 自定义 watchEffect,而该 effect 又读取了 computed
- 用 computed 返回一个大型 reactive 对象,又在外部持续持有该对象引用,间接维持 computed 的活跃状态










