Vue3 的响应式系统源码级嵌套 effect 的实现原理与清理机制
字数 1087 2025-11-16 10:56:36
Vue3 的响应式系统源码级嵌套 effect 的实现原理与清理机制
描述
嵌套 effect 是 Vue3 响应式系统中的核心场景,指 effect 函数(副作用函数)内部触发了另一个 effect 的执行,形成嵌套关系。典型场景如:父组件渲染时创建子组件(子组件的渲染函数作为嵌套 effect)。若处理不当会导致依赖收集混乱、内存泄漏或过度更新。Vue3 通过 activeEffect 栈和 cleanup 机制协同解决此问题。
解题过程
-
嵌套 effect 的问题本质
- 当 effect A 执行时,若内部触发 effect B,B 会覆盖全局的 activeEffect(当前正在处理的副作用)。
- 若 B 执行完成后未恢复 A 为 activeEffect,则后续依赖收集会错误关联到 B,导致 A 的依赖丢失或错误触发。
-
activeEffect 栈管理
- Vue3 使用
effectStack数组(后进先出)缓存正在执行的 effect。 - 执行 effect 前,将其压入栈顶并设为 activeEffect;执行完成后弹出栈顶,并恢复栈顶下一个 effect 为 activeEffect。
let effectStack: ReactiveEffect[] = [] let activeEffect: ReactiveEffect | undefined function run(effect: ReactiveEffect) { if (effectStack.includes(effect)) return // 防止循环 try { effectStack.push(effect) activeEffect = effect return effect.fn() // 执行副作用函数 } finally { effectStack.pop() activeEffect = effectStack[effectStack.length - 1] // 恢复上一个 effect } } - Vue3 使用
-
依赖清理机制(cleanup)
- 每个 effect 实例通过
deps数组记录所有关联的依赖集合(Set)。 - 重新执行 effect 前,先遍历
deps,从每个依赖集合中移除当前 effect(避免遗留依赖)。 - 执行过程中重新收集依赖,确保依赖最新。
class ReactiveEffect { deps: Set<Set<ReactiveEffect>>[] = [] run() { cleanup(this) // 执行前清理旧依赖 return run(this) } } function cleanup(effect: ReactiveEffect) { for (const dep of effect.deps) { dep.delete(effect) // 从依赖集合中移除 effect } effect.deps.length = 0 // 清空 deps 数组 } - 每个 effect 实例通过
-
嵌套 effect 协同流程
- 场景:effect A 执行时,其内部触发 effect B。
- 步骤:
- A 压入栈,activeEffect = A,执行 A.fn()。
- 遇到 B 执行:B 压入栈,activeEffect = B,执行 B.fn()。
- B 触发依赖收集,将 B 添加到对应依赖集合。
- B 执行完成:弹出栈,activeEffect 恢复为 A。
- A 继续执行,后续依赖正确收集到 A。
- 清理机制作用:若 A 重新执行,会先清理 A 的旧依赖(包括可能失效的 B 相关依赖),避免冗余触发。
-
实际应用示例
- 组件渲染:父组件 effect 执行时,遇到子组件渲染函数(嵌套 effect)。
- 计算属性:计算属性内部可能触发其他响应式数据访问,形成嵌套依赖链。
总结
Vue3 通过 effectStack 栈管理嵌套 effect 的活跃状态,确保依赖收集的准确性;通过 cleanup 机制清理旧依赖,避免无效更新。两者协同保证了嵌套场景下响应式系统的可靠性与性能。