refactor(reactivity): optimize child effect scope dereferencing (#4184)
This commit is contained in:
parent
3b38c9ae9b
commit
91f29540fe
@ -77,8 +77,9 @@ describe('reactivity/effect/scope', () => {
|
||||
})
|
||||
})
|
||||
|
||||
expect(scope.effects.length).toBe(2)
|
||||
expect(scope.effects[1]).toBeInstanceOf(EffectScope)
|
||||
expect(scope.effects.length).toBe(1)
|
||||
expect(scope.scopes.length).toBe(1)
|
||||
expect(scope.scopes[0]).toBeInstanceOf(EffectScope)
|
||||
|
||||
expect(dummy).toBe(0)
|
||||
counter.num = 7
|
||||
@ -194,9 +195,9 @@ describe('reactivity/effect/scope', () => {
|
||||
it('should derefence child scope from parent scope after stopping child scope (no memleaks)', async () => {
|
||||
const parent = new EffectScope()
|
||||
const child = parent.run(() => new EffectScope())!
|
||||
expect(parent.effects.includes(child)).toBe(true)
|
||||
expect(parent.scopes.includes(child)).toBe(true)
|
||||
child.stop()
|
||||
expect(parent.effects.includes(child)).toBe(false)
|
||||
expect(parent.scopes.includes(child)).toBe(false)
|
||||
})
|
||||
|
||||
it('test with higher level APIs', async () => {
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { remove } from '@vue/shared'
|
||||
import { ReactiveEffect } from './effect'
|
||||
import { warn } from './warning'
|
||||
|
||||
@ -7,17 +6,23 @@ const effectScopeStack: EffectScope[] = []
|
||||
|
||||
export class EffectScope {
|
||||
active = true
|
||||
effects: (ReactiveEffect | EffectScope)[] = []
|
||||
effects: ReactiveEffect[] = []
|
||||
cleanups: (() => void)[] = []
|
||||
parent: EffectScope | undefined
|
||||
private children: EffectScope[] | undefined
|
||||
private parentIndex: number | undefined
|
||||
|
||||
constructor(detached = false) {
|
||||
if (!detached) {
|
||||
recordEffectScope(this)
|
||||
this.parent = activeEffectScope
|
||||
this.recordEffectScope()
|
||||
}
|
||||
}
|
||||
|
||||
get scopes(): EffectScope[] {
|
||||
if (!this.children) this.children = []
|
||||
return this.children
|
||||
}
|
||||
|
||||
run<T>(fn: () => T): T | undefined {
|
||||
if (this.active) {
|
||||
try {
|
||||
@ -45,15 +50,32 @@ export class EffectScope {
|
||||
}
|
||||
}
|
||||
|
||||
stop(fromParent = false) {
|
||||
stop() {
|
||||
if (this.active) {
|
||||
this.effects.forEach(e => e.stop(true))
|
||||
this.effects.forEach(e => e.stop())
|
||||
this.children?.forEach(e => e.stop())
|
||||
this.cleanups.forEach(cleanup => cleanup())
|
||||
this.parent?.derefChildScope(this)
|
||||
this.active = false
|
||||
if (!fromParent && this.parent) {
|
||||
remove(this.parent.effects, this)
|
||||
}
|
||||
}
|
||||
|
||||
private recordEffectScope() {
|
||||
const parent = activeEffectScope
|
||||
if (parent && parent.active) {
|
||||
this.parent = parent
|
||||
this.parentIndex = parent.scopes.push(this) - 1
|
||||
}
|
||||
}
|
||||
|
||||
private derefChildScope(scope: EffectScope) {
|
||||
// reuse the freed index by moving the last array entry
|
||||
const last = this.scopes.pop()
|
||||
if (last && last !== scope) {
|
||||
const childIndex = scope.parentIndex!
|
||||
this.scopes[childIndex] = last
|
||||
last.parentIndex = childIndex
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -62,7 +84,7 @@ export function effectScope(detached?: boolean) {
|
||||
}
|
||||
|
||||
export function recordEffectScope(
|
||||
effect: ReactiveEffect | EffectScope,
|
||||
effect: ReactiveEffect,
|
||||
scope?: EffectScope | null
|
||||
) {
|
||||
scope = scope || activeEffectScope
|
||||
|
Loading…
x
Reference in New Issue
Block a user