1. 全局变量与基础类定义
activeEffectScope:表示当前正在运行的 effect 作用域。EffectScope类:用来管理一组副作用(ReactiveEffect),提供生命周期控制。
1import type { ReactiveEffect } from './effect' 2import { warn } from './warning' 3 4// 当前全局正在运行的作用域 5export let activeEffectScope: EffectScope | undefined 6 7export class EffectScope { 8 private _active = true // 是否活跃 9 private _on = 0 // on() 调用计数 10 effects: ReactiveEffect[] = [] // 收集的副作用 11 cleanups: (() => void)[] = [] // 清理回调 12 private _isPaused = false // 是否暂停 13 14 parent: EffectScope | undefined // 父作用域 15 scopes: EffectScope[] | undefined // 子作用域 16 private index: number | undefined // 在父作用域数组中的索引 17
2. 构造函数与层级关系
- 构造时会判断是否
detached(独立作用域)。 - 非
detached时,会自动挂到当前activeEffectScope的scopes中,形成父子层级。
1 constructor(public detached = false) { 2 this.parent = activeEffectScope 3 if (!detached && activeEffectScope) { 4 this.index = 5 (activeEffectScope.scopes || (activeEffectScope.scopes = [])).push(this) - 1 6 } 7 } 8 9 get active(): boolean { 10 return this._active 11 } 12
3. 暂停与恢复
pause():暂停本作用域及子作用域的所有副作用。resume():恢复运行。
1 pause(): void { 2 if (this._active) { 3 this._isPaused = true 4 if (this.scopes) { 5 for (let i = 0; i < this.scopes.length; i++) { 6 this.scopes[i].pause() 7 } 8 } 9 for (let i = 0; i < this.effects.length; i++) { 10 this.effects[i].pause() 11 } 12 } 13 } 14 15 resume(): void { 16 if (this._active && this._isPaused) { 17 this._isPaused = false 18 if (this.scopes) { 19 for (let i = 0; i < this.scopes.length; i++) { 20 this.scopes[i].resume() 21 } 22 } 23 for (let i = 0; i < this.effects.length; i++) { 24 this.effects[i].resume() 25 } 26 } 27 } 28
4. 执行函数上下文
run(fn):在当前作用域环境下执行函数。- 内部会切换
activeEffectScope,保证新副作用挂到正确的 scope。
1 run<T>(fn: () => T): T | undefined { 2 if (this._active) { 3 const currentEffectScope = activeEffectScope 4 try { 5 activeEffectScope = this 6 return fn() 7 } finally { 8 activeEffectScope = currentEffectScope 9 } 10 } else if (__DEV__) { 11 warn(`cannot run an inactive effect scope.`) 12 } 13 } 14
5. 手动 on/off 控制
on():进入 scope,记录之前的 scope。off():退出 scope,还原到上一个 scope。
1 prevScope: EffectScope | undefined 2 3 on(): void { 4 if (++this._on === 1) { 5 this.prevScope = activeEffectScope 6 activeEffectScope = this 7 } 8 } 9 10 off(): void { 11 if (this._on > 0 && --this._on === 0) { 12 activeEffectScope = this.prevScope 13 this.prevScope = undefined 14 } 15 } 16
6. 停止(销毁)
stop():彻底销毁本 scope。- 停止所有副作用。
- 调用清理回调。
- 停止所有子 scope。
- 从父作用域中移除自己,避免内存泄漏。
1 stop(fromParent?: boolean): void { 2 if (this._active) { 3 this._active = false 4 for (let i = 0; i < this.effects.length; i++) { 5 this.effects[i].stop() 6 } 7 this.effects.length = 0 8 9 for (let i = 0; i < this.cleanups.length; i++) { 10 this.cleanups[i]() 11 } 12 this.cleanups.length = 0 13 14 if (this.scopes) { 15 for (let i = 0; i < this.scopes.length; i++) { 16 this.scopes[i].stop(true) 17 } 18 this.scopes.length = 0 19 } 20 21 if (!this.detached && this.parent && !fromParent) { 22 const last = this.parent.scopes!.pop() 23 if (last && last !== this) { 24 this.parent.scopes![this.index!] = last 25 last.index = this.index! 26 } 27 } 28 this.parent = undefined 29 } 30 } 31} 32
7. 工具函数
effectScope(detached):创建新的作用域。getCurrentScope():获取当前活跃作用域。onScopeDispose(fn):在当前作用域注册清理函数。
1export function effectScope(detached?: boolean): EffectScope { 2 return new EffectScope(detached) 3} 4 5export function getCurrentScope(): EffectScope | undefined { 6 return activeEffectScope 7} 8 9export function onScopeDispose(fn: () => void, failSilently = false): void { 10 if (activeEffectScope) { 11 activeEffectScope.cleanups.push(fn) 12 } else if (__DEV__ && !failSilently) { 13 warn( 14 `onScopeDispose() is called when there is no active effect scope` + 15 ` to be associated with.`, 16 ) 17 } 18} 19
使用示例:简化版 effectScope
在 Vue3 中,setup() 里创建的副作用(watch/computed)会自动挂到组件的 scope 上。这里我们用手动的 effectScope 来演示:
1import { ref, watch, effectScope } from 'vue' 2 3const scope = effectScope() 4 5scope.run(() => { 6 const count = ref(0) 7 8 // 这个 watch 会被 scope 收集 9 watch(count, (newVal) => { 10 console.log('count changed:', newVal) 11 }) 12 13 // 模拟修改 14 count.value++ 15 count.value++ 16}) 17 18// 当我们不需要这个 scope 里的副作用时 19scope.stop() 20// 此时 watch 会被自动清理,不会再触发 21
✅ 这样一篇文章逻辑清晰,最后有示例,能从源码理解到实际应用。
本文内容由人工智能生成,仅供学习与参考使用,请在实际应用中结合自身情况进行判断。
《Vue3 EffectScope 源码解析与理解》 是转载文章,点击查看原文。