Vue3 的响应式系统嵌套 effect 的实现原理与清理机制
字数 1147 2025-11-07 22:15:36

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

1. 问题描述

在 Vue3 的响应式系统中,effect 可能会嵌套执行(例如组件渲染时触发了另一个 effect)。若不正确处理嵌套依赖,会导致依赖收集混乱(如父 effect 依赖了子 effect 的属性)。Vue3 通过 effect 栈activeEffect 切换机制 解决此问题。


2. 核心概念:effect 栈与活跃 effect

  • effect 栈(effectStack):用于存储正在执行的 effect 函数,按执行顺序入栈/出栈。
  • activeEffect:当前正在处理的活跃 effect,依赖收集时关联到该变量。

3. 嵌套 effect 的执行流程

假设如下代码:

effect(() => {  
  console.log("父 effect 执行");  
  effect(() => {  
    console.log("子 effect 执行");  
  });  
});  

步骤 1:初始化执行

  • 外层 effect 被推入 effect 栈,activeEffect 指向父 effect。
  • 开始执行父 effect 的函数体。

步骤 2:内层 effect 执行

  • 遇到内层 effect 时,先将父 effect 的执行暂停,内层 effect 入栈,activeEffect 切换为子 effect。
  • 执行子 effect 的函数体,收集子 effect 的依赖。

步骤 3:内层执行完毕

  • 子 effect 执行结束,出栈,activeEffect 恢复为父 effect。
  • 继续执行父 effect 的剩余代码。

关键点:

通过栈结构保证 activeEffect 始终指向当前正在执行的 effect,避免依赖收集错乱。


4. 依赖清理机制(避免遗留依赖)

当 effect 重新执行时,需要先清理旧依赖,再收集新依赖。Vue3 通过 deps 数组实现反向清理。

步骤 1:依赖记录

每个 effect 实例维护一个 deps 数组,存储所有关联的依赖集合(即响应式属性对应的 Set)。

步骤 2:清理旧依赖

effect 重新执行前,遍历 deps,从每个依赖集合中删除该 effect,避免遗留无效依赖。

步骤 3:重新收集

执行 effect 函数时,依赖被重新添加到最新的依赖集合中。


5. 源码级示例(简化版)

let activeEffect;  
const effectStack = [];  

class ReactiveEffect {  
  constructor(fn) {  
    this.fn = fn;  
    this.deps = []; // 存储依赖集合  
  }  
  run() {  
    // 清理旧依赖  
    cleanupEffect(this);  
    // 压栈并切换活跃 effect  
    effectStack.push(this);  
    activeEffect = this;  
    // 执行函数(触发依赖收集)  
    this.fn();  
    // 出栈并恢复上一个 effect  
    effectStack.pop();  
    activeEffect = effectStack[effectStack.length - 1];  
  }  
}  

function cleanupEffect(effect) {  
  for (const dep of effect.deps) {  
    dep.delete(effect); // 从依赖集合中移除该 effect  
  }  
  effect.deps.length = 0; // 清空 deps 数组  
}  

6. 实际场景:组件渲染嵌套

  • 父组件渲染时触发父 effect,子组件渲染触发子 effect。
  • 通过 effect 栈确保子组件依赖正确关联到子 effect,而非父 effect。

7. 总结

  • 嵌套 effect:通过栈结构管理 activeEffect,保证依赖收集准确。
  • 依赖清理:通过 deps 反向关联,避免无效依赖残留。
  • 性能优化:避免不必要的依赖关联,提升响应式系统效率。
Vue3 的响应式系统嵌套 effect 的实现原理与清理机制 1. 问题描述 在 Vue3 的响应式系统中, effect 可能会嵌套执行(例如组件渲染时触发了另一个 effect )。若不正确处理嵌套依赖,会导致依赖收集混乱(如父 effect 依赖了子 effect 的属性)。Vue3 通过 effect 栈 和 activeEffect 切换机制 解决此问题。 2. 核心概念:effect 栈与活跃 effect effect 栈(effectStack) :用于存储正在执行的 effect 函数,按执行顺序入栈/出栈。 activeEffect :当前正在处理的活跃 effect,依赖收集时关联到该变量。 3. 嵌套 effect 的执行流程 假设如下代码: 步骤 1:初始化执行 外层 effect 被推入 effect 栈, activeEffect 指向父 effect。 开始执行父 effect 的函数体。 步骤 2:内层 effect 执行 遇到内层 effect 时, 先将父 effect 的执行暂停 ,内层 effect 入栈, activeEffect 切换为子 effect。 执行子 effect 的函数体,收集子 effect 的依赖。 步骤 3:内层执行完毕 子 effect 执行结束,出栈, activeEffect 恢复为父 effect。 继续执行父 effect 的剩余代码。 关键点: 通过栈结构保证 activeEffect 始终指向当前正在执行的 effect,避免依赖收集错乱。 4. 依赖清理机制(避免遗留依赖) 当 effect 重新执行时,需要先清理旧依赖,再收集新依赖。Vue3 通过 deps 数组实现反向清理。 步骤 1:依赖记录 每个 effect 实例维护一个 deps 数组,存储所有关联的依赖集合(即响应式属性对应的 Set )。 步骤 2:清理旧依赖 effect 重新执行前,遍历 deps ,从每个依赖集合中删除该 effect,避免遗留无效依赖。 步骤 3:重新收集 执行 effect 函数时,依赖被重新添加到最新的依赖集合中。 5. 源码级示例(简化版) 6. 实际场景:组件渲染嵌套 父组件渲染时触发父 effect,子组件渲染触发子 effect。 通过 effect 栈确保子组件依赖正确关联到子 effect,而非父 effect。 7. 总结 嵌套 effect :通过栈结构管理 activeEffect ,保证依赖收集准确。 依赖清理 :通过 deps 反向关联,避免无效依赖残留。 性能优化 :避免不必要的依赖关联,提升响应式系统效率。