Vue3 的响应式系统源码级 effectScope 的实现原理与作用域管理
字数 1182 2025-11-30 01:42:00
Vue3 的响应式系统源码级 effectScope 的实现原理与作用域管理
1. 问题背景
在 Vue3 的响应式系统中,副作用(effect)通常需要手动管理其生命周期(如组件卸载时清理副作用)。当多个 effect 需要统一管理时,逐个清理会变得繁琐。effectScope 是 Vue3.2 引入的 API,用于批量管理一组 effect 的生命周期,提升可维护性并避免内存泄漏。
2. effectScope 的核心作用
- 批量收集 effect:在作用域内创建的 effect 会被自动关联到该作用域。
- 统一清理:调用
scope.stop()可一次性清除所有关联的 effect。 - 嵌套作用域:支持父子层级的作用域,停止父作用域时会递归停止子作用域。
3. 实现原理详解
3.1 作用域的数据结构
class EffectScope {
active = true;
effects: ReactiveEffect[] = []; // 收集当前作用域的 effect
cleanups: (() => void)[] = []; // 用户自定义清理函数
parent: EffectScope | undefined;
scopes: EffectScope[] | undefined; // 子作用域
}
3.2 全局作用域栈管理
Vue 维护一个全局栈 effectScopeStack,用于跟踪当前活跃的作用域:
let activeEffectScope: EffectScope | undefined;
const effectScopeStack: EffectScope[] = [];
3.3 作用域创建与进入
当执行 effectScope() 时:
- 创建新的
EffectScope实例。 - 若存在父作用域(
activeEffectScope),将新作用域添加到父作用域的scopes中。 - 通过
enterScope(scope)将作用域压入栈并设为当前活跃作用域:function enterScope(scope: EffectScope) { if (activeEffectScope) { effectScopeStack.push(activeEffectScope); } activeEffectScope = scope; }
3.4 effect 的自动关联
在创建 effect 时(如 reactive、watch、computed),会检查 activeEffectScope:
function createReactiveEffect(fn, options) {
const effect = new ReactiveEffect(fn);
if (activeEffectScope && activeEffectScope.active) {
activeEffectScope.effects.push(effect); // 自动收集到当前作用域
}
return effect;
}
3.5 作用域的停止(stop)
调用 scope.stop() 时:
- 标记作用域为未激活状态(
active = false)。 - 递归停止所有子作用域(
scopes?.forEach(s => s.stop()))。 - 销毁所有关联的 effect:
for (const effect of this.effects) { effect.stop(); // 触发 effect 的清理逻辑(如移除依赖追踪) } - 执行用户通过
onScopeDispose注册的清理函数(cleanups)。
3.6 嵌套作用域的示例
const parent = effectScope();
const child = effectScope();
parent.run(() => {
child.run(() => {
watch(data, () => console.log("child effect"));
});
watch(data, () => console.log("parent effect"));
});
parent.stop(); // 会同时停止 parent 和 child 内的所有 effect
4. 在 Vue 组件中的应用
Vue 组件实例内部维护一个 _scope 属性,在 setup() 执行期间:
- 创建组件级作用域并设为当前活跃作用域。
- 组件卸载时自动调用
scope.stop(),清理所有副作用(如watch、computed)。
5. 总结
- 依赖收集:通过全局栈管理作用域层级,effect 自动注册到当前作用域。
- 生命周期:作用域停止时递归清理 effect 和子作用域,避免内存泄漏。
- 组件集成:简化了组件副作用的批量管理,无需手动跟踪每个 effect。
通过 effectScope,Vue3 提供了响应式副作用的模块化治理能力,尤其适用于复杂逻辑的复用(如 Composables 开发)。