소스 검색

refactor(reactivity): optimize child effect scope dereferencing (#4184)

Bas van Meurs 4 년 전
부모
커밋
91f29540fe
2개의 변경된 파일37개의 추가작업 그리고 14개의 파일을 삭제
  1. 5 4
      packages/reactivity/__tests__/effectScope.spec.ts
  2. 32 10
      packages/reactivity/src/effectScope.ts

+ 5 - 4
packages/reactivity/__tests__/effectScope.spec.ts

@@ -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 () => {

+ 32 - 10
packages/reactivity/src/effectScope.ts

@@ -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,14 +50,31 @@ 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