diff --git a/packages/reactivity/src/effectScope.ts b/packages/reactivity/src/effectScope.ts index 92ad92c1249..4b6c0b7c6a4 100644 --- a/packages/reactivity/src/effectScope.ts +++ b/packages/reactivity/src/effectScope.ts @@ -32,21 +32,24 @@ export class EffectScope { * record undetached scopes * @internal */ - scopes: EffectScope[] | undefined + scopes: EffectScope | undefined + scopesTail: EffectScope | undefined /** - * track a child scope's index in its parent's scopes array for optimized - * removal - * @internal + * sibling scope */ - private index: number | undefined + prevEffectScope: EffectScope | undefined + nextEffectScope: EffectScope | undefined constructor(public detached = false) { this.parent = activeEffectScope if (!detached && activeEffectScope) { - this.index = - (activeEffectScope.scopes || (activeEffectScope.scopes = [])).push( - this, - ) - 1 + if (activeEffectScope.scopesTail) { + this.prevEffectScope = activeEffectScope.scopesTail + activeEffectScope.scopesTail.nextEffectScope = this + activeEffectScope.scopesTail = this + } else { + activeEffectScope.scopes = activeEffectScope.scopesTail = this + } } } @@ -57,13 +60,14 @@ export class EffectScope { pause(): void { if (this._active) { this._isPaused = true - let i, l - if (this.scopes) { - for (i = 0, l = this.scopes.length; i < l; i++) { - this.scopes[i].pause() - } + for ( + let child = this.scopes; + child != undefined; + child = child.nextEffectScope + ) { + child.pause() } - for (i = 0, l = this.effects.length; i < l; i++) { + for (let i = 0, l = this.effects.length; i < l; i++) { this.effects[i].pause() } } @@ -76,13 +80,14 @@ export class EffectScope { if (this._active) { if (this._isPaused) { this._isPaused = false - let i, l - if (this.scopes) { - for (i = 0, l = this.scopes.length; i < l; i++) { - this.scopes[i].resume() - } + for ( + let child = this.scopes; + child != undefined; + child = child.nextEffectScope + ) { + child.resume() } - for (i = 0, l = this.effects.length; i < l; i++) { + for (let i = 0, l = this.effects.length; i < l; i++) { this.effects[i].resume() } } @@ -140,20 +145,28 @@ export class EffectScope { } this.cleanups.length = 0 - if (this.scopes) { - for (i = 0, l = this.scopes.length; i < l; i++) { - this.scopes[i].stop(true) - } - this.scopes.length = 0 + for ( + let child = this.scopes; + child != undefined; + child = child.nextEffectScope + ) { + child.stop(true) } + this.scopes = this.scopesTail = undefined // nested scope, dereference from parent to avoid memory leaks if (!this.detached && this.parent && !fromParent) { - // optimized O(1) removal - const last = this.parent.scopes!.pop() - if (last && last !== this) { - this.parent.scopes![this.index!] = last - last.index = this.index! + if (this.prevEffectScope) { + this.prevEffectScope.nextEffectScope = this.nextEffectScope + } + if (this.nextEffectScope) { + this.nextEffectScope.prevEffectScope = this.prevEffectScope + } + if (this.parent.scopes == this) { + this.parent.scopes = this.nextEffectScope + } + if (this.parent.scopesTail == this) { + this.parent.scopesTail = this.prevEffectScope } } this.parent = undefined