2021-07-07 21:07:19 +08:00
|
|
|
import type { ReactiveEffect } from './effect'
|
|
|
|
import { warn } from './warning'
|
|
|
|
|
2024-03-07 17:53:10 +08:00
|
|
|
export let activeEffectScope: EffectScope | undefined
|
2021-07-07 21:07:19 +08:00
|
|
|
|
|
|
|
export class EffectScope {
|
2022-04-12 15:56:57 +08:00
|
|
|
/**
|
|
|
|
* @internal
|
|
|
|
*/
|
2022-11-14 09:27:52 +08:00
|
|
|
private _active = true
|
2022-04-12 15:56:57 +08:00
|
|
|
/**
|
|
|
|
* @internal
|
|
|
|
*/
|
2021-07-29 22:26:24 +08:00
|
|
|
effects: ReactiveEffect[] = []
|
2022-04-12 15:56:57 +08:00
|
|
|
/**
|
|
|
|
* @internal
|
|
|
|
*/
|
2021-07-07 21:07:19 +08:00
|
|
|
cleanups: (() => void)[] = []
|
2021-07-29 22:45:05 +08:00
|
|
|
|
2022-04-12 15:51:05 +08:00
|
|
|
/**
|
2022-05-10 10:51:51 +08:00
|
|
|
* only assigned by undetached scope
|
2022-04-12 15:56:57 +08:00
|
|
|
* @internal
|
2022-04-12 15:51:05 +08:00
|
|
|
*/
|
2021-07-29 00:08:01 +08:00
|
|
|
parent: EffectScope | undefined
|
2022-04-12 15:51:05 +08:00
|
|
|
/**
|
|
|
|
* record undetached scopes
|
2022-04-12 15:56:57 +08:00
|
|
|
* @internal
|
2022-04-12 15:51:05 +08:00
|
|
|
*/
|
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
|
2022-04-12 15:56:57 +08:00
|
|
|
* @internal
|
2021-07-29 22:45:05 +08:00
|
|
|
*/
|
|
|
|
private index: number | undefined
|
2021-07-07 21:07:19 +08:00
|
|
|
|
2022-10-14 10:53:23 +08:00
|
|
|
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
|
2021-07-07 21:07:19 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-14 09:27:52 +08:00
|
|
|
get active() {
|
|
|
|
return this._active
|
|
|
|
}
|
|
|
|
|
2021-07-07 21:07:19 +08:00
|
|
|
run<T>(fn: () => T): T | undefined {
|
2022-11-14 09:27:52 +08:00
|
|
|
if (this._active) {
|
2022-04-12 15:51:05 +08:00
|
|
|
const currentEffectScope = activeEffectScope
|
2021-07-07 21:07:19 +08:00
|
|
|
try {
|
2022-01-28 21:02:09 +08:00
|
|
|
activeEffectScope = this
|
2021-07-07 21:07:19 +08:00
|
|
|
return fn()
|
|
|
|
} finally {
|
2022-04-12 15:51:05 +08:00
|
|
|
activeEffectScope = currentEffectScope
|
2021-07-07 21:07:19 +08:00
|
|
|
}
|
|
|
|
} else if (__DEV__) {
|
|
|
|
warn(`cannot run an inactive effect scope.`)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-12 15:56:57 +08:00
|
|
|
/**
|
|
|
|
* This should only be called on non-detached scopes
|
|
|
|
* @internal
|
|
|
|
*/
|
2021-07-07 21:07:19 +08:00
|
|
|
on() {
|
2022-01-28 21:02:09 +08:00
|
|
|
activeEffectScope = this
|
2021-07-07 21:07:19 +08:00
|
|
|
}
|
|
|
|
|
2022-04-12 15:56:57 +08:00
|
|
|
/**
|
|
|
|
* This should only be called on non-detached scopes
|
|
|
|
* @internal
|
|
|
|
*/
|
2021-07-07 21:07:19 +08:00
|
|
|
off() {
|
2022-01-28 21:02:09 +08:00
|
|
|
activeEffectScope = this.parent
|
2021-07-07 21:07:19 +08:00
|
|
|
}
|
|
|
|
|
2021-07-29 22:45:05 +08:00
|
|
|
stop(fromParent?: boolean) {
|
2022-11-14 09:27:52 +08:00
|
|
|
if (this._active) {
|
2022-01-28 18:35:09 +08:00
|
|
|
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) {
|
2022-01-28 18:35:09 +08:00
|
|
|
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
|
2022-10-14 10:53:23 +08:00
|
|
|
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!
|
|
|
|
}
|
|
|
|
}
|
2022-10-14 10:53:23 +08:00
|
|
|
this.parent = undefined
|
2022-11-14 09:27:52 +08:00
|
|
|
this._active = false
|
2021-07-29 22:26:24 +08:00
|
|
|
}
|
|
|
|
}
|
2021-07-07 21:07:19 +08:00
|
|
|
}
|
|
|
|
|
2023-03-31 17:06:10 +08:00
|
|
|
/**
|
|
|
|
* Creates an effect scope object which can capture the reactive effects (i.e.
|
|
|
|
* computed and watchers) created within it so that these effects can be
|
|
|
|
* disposed together. For detailed use cases of this API, please consult its
|
|
|
|
* corresponding {@link https://github.com/vuejs/rfcs/blob/master/active-rfcs/0041-reactivity-effect-scope.md | RFC}.
|
|
|
|
*
|
|
|
|
* @param detached - Can be used to create a "detached" effect scope.
|
|
|
|
* @see {@link https://vuejs.org/api/reactivity-advanced.html#effectscope}
|
|
|
|
*/
|
2021-07-07 21:07:19 +08:00
|
|
|
export function effectScope(detached?: boolean) {
|
|
|
|
return new EffectScope(detached)
|
|
|
|
}
|
|
|
|
|
2023-03-31 17:06:10 +08:00
|
|
|
/**
|
|
|
|
* Returns the current active effect scope if there is one.
|
|
|
|
*
|
|
|
|
* @see {@link https://vuejs.org/api/reactivity-advanced.html#getcurrentscope}
|
|
|
|
*/
|
2021-07-07 21:07:19 +08:00
|
|
|
export function getCurrentScope() {
|
|
|
|
return activeEffectScope
|
|
|
|
}
|
|
|
|
|
2023-03-31 17:06:10 +08:00
|
|
|
/**
|
|
|
|
* Registers a dispose callback on the current active effect scope. The
|
|
|
|
* callback will be invoked when the associated effect scope is stopped.
|
|
|
|
*
|
|
|
|
* @param fn - The callback function to attach to the scope's cleanup.
|
|
|
|
* @see {@link https://vuejs.org/api/reactivity-advanced.html#onscopedispose}
|
|
|
|
*/
|
2024-03-07 17:54:18 +08:00
|
|
|
export function onScopeDispose(fn: () => void, failSilently = false) {
|
2021-07-07 21:07:19 +08:00
|
|
|
if (activeEffectScope) {
|
|
|
|
activeEffectScope.cleanups.push(fn)
|
2024-03-07 17:54:18 +08:00
|
|
|
} else if (__DEV__ && !failSilently) {
|
2021-07-07 21:07:19 +08:00
|
|
|
warn(
|
2021-08-17 06:19:06 +08:00
|
|
|
`onScopeDispose() is called when there is no active effect scope` +
|
2021-07-07 21:07:19 +08:00
|
|
|
` to be associated with.`,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|