Vue3 的响应式系统源码级 effect 的 active 状态与依赖清理机制
字数 1427 2025-12-12 01:27:01
Vue3 的响应式系统源码级 effect 的 active 状态与依赖清理机制
描述:
在 Vue3 的响应式系统中,effect 是响应式副作用的核心抽象,用于追踪依赖并在数据变化时重新执行。其中 active 状态用于控制 effect 是否参与依赖收集,而依赖清理机制确保在 effect 重新执行前正确清理旧依赖,避免无效更新和内存泄漏。理解这两个机制对于掌握 effect 的生命周期和性能优化至关重要。
循序渐进讲解:
-
effect 的基本结构
Vue3 中每个 effect 是一个封装了用户副作用函数的对象,包含以下关键属性:const effect = { fn, // 用户传入的副作用函数 scheduler, // 调度器(可选) active: true, // 是否激活 deps: [] // 依赖列表(存储了所有包含此 effect 的依赖集合) }active初始为true,表示 effect 处于激活状态,会正常追踪依赖。deps是一个数组,存储了所有“依赖集合”(即包含此 effect 的Set<effect>)。例如,当响应式对象属性obj.a被访问时,会将当前 effect 添加到obj.a对应的依赖集合中,同时将该集合记录到 effect.deps 中。
-
active 状态的作用
- 激活状态(active: true):effect 会被
track收集到依赖关系中,且在trigger时会被执行或调度。 - 非激活状态(active: false):effect 不会被
track收集,也不会被trigger触发。这通常发生在:- effect 被手动停止(
stop被调用)。 - 组件卸载时,组件相关的 effect 会被停止。
- effect 被手动停止(
- 示例:调用
runner.effect.stop()会将active设为false,并清空依赖关系。
- 激活状态(active: true):effect 会被
-
依赖清理(cleanup)的必要性
考虑以下场景:const obj = reactive({ a: 1, b: 2 }) effect(() => { console.log(obj.a ? obj.b : 0) }) // 首次执行:访问 obj.a 和 obj.b,收集依赖 obj.a = 0 // 触发 effect 重新执行 // 第二次执行:因 obj.a 为 0,只访问 obj.a,不再访问 obj.b- 第一次执行后,effect 被收集到
obj.a和obj.b的依赖集合中。 - 当
obj.a变为 0 后,第二次执行时不再访问obj.b。如果不清理,那么obj.b的依赖集合中仍保留此 effect,导致后续obj.b变化时仍会触发 effect(无效更新)。
- 第一次执行后,effect 被收集到
-
cleanup 实现机制
在 effect 执行前,Vue3 会执行清理操作:function cleanup(effect) { const { deps } = effect if (deps.length) { for (let i = 0; i < deps.length; i++) { deps[i].delete(effect) // 从每个依赖集合中移除当前 effect } deps.length = 0 // 清空 deps 数组 } }- 遍历
effect.deps,从每个依赖集合(Set<effect>)中删除当前 effect。 - 清空
effect.deps数组,为新一轮依赖收集做准备。
- 遍历
-
cleanup 的执行时机
在effect.run()或effect.scheduler()中,执行用户副作用函数前会先调用cleanup:function run() { if (!this.active) return cleanup(this) // 执行清理 // 设置当前激活的 effect,执行用户函数,收集新依赖... }- 清理 → 执行用户函数 → 重新收集依赖,形成一个闭环。
-
active 与 cleanup 的协同
- 当 effect 被停止(
active = false)时,也会调用cleanup清空所有依赖,确保无残留引用。 - 如果 effect 已非激活状态,即使被
trigger也不会执行,因为run函数会提前返回。
- 当 effect 被停止(
-
实际应用示例
const runner = effect(() => { ... }) // 一段时间后停止 effect runner.effect.stop() // 此时 active = false,依赖被清理,后续数据变化不再触发此 effect
总结:
active 状态控制了 effect 的“生死”,决定其是否参与响应式系统。cleanup 机制确保了每次重新执行前清理旧依赖,避免无效更新和内存泄漏。两者结合保证了 effect 在动态依赖场景下的正确性和性能,是 Vue3 响应式系统健壮性的基石。