@@ -8,21 +8,25 @@ export class EffectScope {
8
8
active = true
9
9
effects : ReactiveEffect [ ] = [ ]
10
10
cleanups : ( ( ) => void ) [ ] = [ ]
11
+
11
12
parent : EffectScope | undefined
12
- private children : EffectScope [ ] | undefined
13
- private parentIndex : number | undefined
13
+ scopes : EffectScope [ ] | undefined
14
+ /**
15
+ * track a child scope's index in its parent's scopes array for optimized
16
+ * removal
17
+ */
18
+ private index : number | undefined
14
19
15
20
constructor ( detached = false ) {
16
- if ( ! detached ) {
17
- this . recordEffectScope ( )
21
+ if ( ! detached && activeEffectScope ) {
22
+ this . parent = activeEffectScope
23
+ this . index =
24
+ ( activeEffectScope . scopes || ( activeEffectScope . scopes = [ ] ) ) . push (
25
+ this
26
+ ) - 1
18
27
}
19
28
}
20
29
21
- get scopes ( ) : EffectScope [ ] {
22
- if ( ! this . children ) this . children = [ ]
23
- return this . children
24
- }
25
-
26
30
run < T > ( fn : ( ) => T ) : T | undefined {
27
31
if ( this . active ) {
28
32
try {
@@ -50,33 +54,25 @@ export class EffectScope {
50
54
}
51
55
}
52
56
53
- stop ( ) {
57
+ stop ( fromParent ?: boolean ) {
54
58
if ( this . active ) {
55
59
this . effects . forEach ( e => e . stop ( ) )
56
- this . children ?. forEach ( e => e . stop ( ) )
57
60
this . cleanups . forEach ( cleanup => cleanup ( ) )
58
- this . parent ?. derefChildScope ( this )
61
+ if ( this . scopes ) {
62
+ this . scopes . forEach ( e => e . stop ( true ) )
63
+ }
64
+ // nested scope, dereference from parent to avoid memory leaks
65
+ if ( this . parent && ! fromParent ) {
66
+ // optimized O(1) removal
67
+ const last = this . parent . scopes ! . pop ( )
68
+ if ( last && last !== this ) {
69
+ this . parent . scopes ! [ this . index ! ] = last
70
+ last . index = this . index !
71
+ }
72
+ }
59
73
this . active = false
60
74
}
61
75
}
62
-
63
- private recordEffectScope ( ) {
64
- const parent = activeEffectScope
65
- if ( parent && parent . active ) {
66
- this . parent = parent
67
- this . parentIndex = parent . scopes . push ( this ) - 1
68
- }
69
- }
70
-
71
- private derefChildScope ( scope : EffectScope ) {
72
- // reuse the freed index by moving the last array entry
73
- const last = this . scopes . pop ( )
74
- if ( last && last !== scope ) {
75
- const childIndex = scope . parentIndex !
76
- this . scopes [ childIndex ] = last
77
- last . parentIndex = childIndex
78
- }
79
- }
80
76
}
81
77
82
78
export function effectScope ( detached ?: boolean ) {
0 commit comments