Vue3 的响应式系统源码级嵌套 effect 的实现原理与清理机制
字数 1087 2025-11-16 10:56:36

Vue3 的响应式系统源码级嵌套 effect 的实现原理与清理机制

描述
嵌套 effect 是 Vue3 响应式系统中的核心场景,指 effect 函数(副作用函数)内部触发了另一个 effect 的执行,形成嵌套关系。典型场景如:父组件渲染时创建子组件(子组件的渲染函数作为嵌套 effect)。若处理不当会导致依赖收集混乱、内存泄漏或过度更新。Vue3 通过 activeEffect 栈和 cleanup 机制协同解决此问题。

解题过程

  1. 嵌套 effect 的问题本质

    • 当 effect A 执行时,若内部触发 effect B,B 会覆盖全局的 activeEffect(当前正在处理的副作用)。
    • 若 B 执行完成后未恢复 A 为 activeEffect,则后续依赖收集会错误关联到 B,导致 A 的依赖丢失或错误触发。
  2. 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  
      }  
    }  
    
  3. 依赖清理机制(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 数组  
    }  
    
  4. 嵌套 effect 协同流程

    • 场景:effect A 执行时,其内部触发 effect B。
    • 步骤
      1. A 压入栈,activeEffect = A,执行 A.fn()。
      2. 遇到 B 执行:B 压入栈,activeEffect = B,执行 B.fn()。
      3. B 触发依赖收集,将 B 添加到对应依赖集合。
      4. B 执行完成:弹出栈,activeEffect 恢复为 A。
      5. A 继续执行,后续依赖正确收集到 A。
    • 清理机制作用:若 A 重新执行,会先清理 A 的旧依赖(包括可能失效的 B 相关依赖),避免冗余触发。
  5. 实际应用示例

    • 组件渲染:父组件 effect 执行时,遇到子组件渲染函数(嵌套 effect)。
    • 计算属性:计算属性内部可能触发其他响应式数据访问,形成嵌套依赖链。

总结
Vue3 通过 effectStack 栈管理嵌套 effect 的活跃状态,确保依赖收集的准确性;通过 cleanup 机制清理旧依赖,避免无效更新。两者协同保证了嵌套场景下响应式系统的可靠性与性能。

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。 依赖清理机制(cleanup) 每个 effect 实例通过 deps 数组记录所有关联的依赖集合(Set)。 重新执行 effect 前,先遍历 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 机制清理旧依赖,避免无效更新。两者协同保证了嵌套场景下响应式系统的可靠性与性能。