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