vue3-core/packages/reactivity/src/effectScope.ts

133 lines
2.9 KiB
TypeScript
Raw Normal View History

import { ReactiveEffect } from './effect'
import { warn } from './warning'
let activeEffectScope: EffectScope | undefined
export class EffectScope {
/**
* @internal
*/
active = true
/**
* @internal
*/
effects: ReactiveEffect[] = []
/**
* @internal
*/
cleanups: (() => void)[] = []
2021-07-29 22:45:05 +08:00
/**
2022-05-10 10:51:51 +08:00
* only assigned by undetached scope
* @internal
*/
parent: EffectScope | undefined
/**
* record undetached scopes
* @internal
*/
2021-07-29 22:45:05 +08:00
scopes: EffectScope[] | undefined
/**
* track a child scope's index in its parent's scopes array for optimized
* removal
* @internal
2021-07-29 22:45:05 +08:00
*/
private index: number | undefined
constructor(public detached = false) {
this.parent = activeEffectScope
2021-07-29 22:45:05 +08:00
if (!detached && activeEffectScope) {
this.index =
(activeEffectScope.scopes || (activeEffectScope.scopes = [])).push(
this
) - 1
}
}
run<T>(fn: () => T): T | undefined {
if (this.active) {
const currentEffectScope = activeEffectScope
try {
2022-01-28 21:02:09 +08:00
activeEffectScope = this
return fn()
} finally {
activeEffectScope = currentEffectScope
}
} else if (__DEV__) {
warn(`cannot run an inactive effect scope.`)
}
}
/**
* This should only be called on non-detached scopes
* @internal
*/
on() {
2022-01-28 21:02:09 +08:00
activeEffectScope = this
}
/**
* This should only be called on non-detached scopes
* @internal
*/
off() {
2022-01-28 21:02:09 +08:00
activeEffectScope = this.parent
}
2021-07-29 22:45:05 +08:00
stop(fromParent?: boolean) {
if (this.active) {
let i, l
for (i = 0, l = this.effects.length; i < l; i++) {
this.effects[i].stop()
}
for (i = 0, l = this.cleanups.length; i < l; i++) {
this.cleanups[i]()
}
2021-07-29 22:45:05 +08:00
if (this.scopes) {
for (i = 0, l = this.scopes.length; i < l; i++) {
this.scopes[i].stop(true)
}
2021-07-29 22:45:05 +08:00
}
// nested scope, dereference from parent to avoid memory leaks
if (!this.detached && this.parent && !fromParent) {
2021-07-29 22:45:05 +08:00
// optimized O(1) removal
const last = this.parent.scopes!.pop()
if (last && last !== this) {
this.parent.scopes![this.index!] = last
last.index = this.index!
}
}
this.parent = undefined
this.active = false
}
}
}
export function effectScope(detached?: boolean) {
return new EffectScope(detached)
}
export function recordEffectScope(
effect: ReactiveEffect,
scope: EffectScope | undefined = activeEffectScope
) {
if (scope && scope.active) {
scope.effects.push(effect)
}
}
export function getCurrentScope() {
return activeEffectScope
}
export function onScopeDispose(fn: () => void) {
if (activeEffectScope) {
activeEffectScope.cleanups.push(fn)
} else if (__DEV__) {
warn(
`onScopeDispose() is called when there is no active effect scope` +
` to be associated with.`
)
}
}