1
1
import { TrackOpTypes , TriggerOpTypes } from './operations'
2
2
import { extend , isArray , isIntegerKey , isMap } from '@vue/shared'
3
3
import { EffectScope , recordEffectScope } from './effectScope'
4
+ import {
5
+ createDep ,
6
+ Dep ,
7
+ newTracked ,
8
+ resetTracked ,
9
+ setNewTracked ,
10
+ setWasTracked ,
11
+ wasTracked
12
+ } from './Dep'
4
13
5
14
// The main WeakMap that stores {target -> key -> dep} connections.
6
15
// Conceptually, it's easier to think of a dependency as a Dep class
7
16
// which maintains a Set of subscribers, but we simply store them as
8
17
// raw Sets to reduce memory overhead.
9
- type Dep = Set < ReactiveEffect >
10
18
type KeyToDepMap = Map < any , Dep >
11
19
const targetMap = new WeakMap < any , KeyToDepMap > ( )
12
20
@@ -56,19 +64,57 @@ export class ReactiveEffect<T = any> {
56
64
return this . fn ( )
57
65
}
58
66
if ( ! effectStack . includes ( this ) ) {
59
- this . cleanup ( )
60
67
try {
61
- enableTracking ( )
62
68
effectStack . push ( ( activeEffect = this ) )
69
+ enableTracking ( )
70
+
71
+ effectTrackDepth ++
72
+
73
+ if ( effectTrackDepth <= maxMarkerBits ) {
74
+ this . initDepMarkers ( )
75
+ } else {
76
+ this . cleanup ( )
77
+ }
63
78
return this . fn ( )
64
79
} finally {
65
- effectStack . pop ( )
80
+ if ( effectTrackDepth <= maxMarkerBits ) {
81
+ this . finalizeDepMarkers ( )
82
+ }
83
+ effectTrackDepth --
66
84
resetTracking ( )
67
- activeEffect = effectStack [ effectStack . length - 1 ]
85
+ effectStack . pop ( )
86
+ const n = effectStack . length
87
+ activeEffect = n > 0 ? effectStack [ n - 1 ] : undefined
68
88
}
69
89
}
70
90
}
71
91
92
+ initDepMarkers ( ) {
93
+ const { deps } = this
94
+ if ( deps . length ) {
95
+ for ( let i = 0 ; i < deps . length ; i ++ ) {
96
+ setWasTracked ( deps [ i ] )
97
+ }
98
+ }
99
+ }
100
+
101
+ finalizeDepMarkers ( ) {
102
+ const { deps } = this
103
+ if ( deps . length ) {
104
+ let ptr = 0
105
+ for ( let i = 0 ; i < deps . length ; i ++ ) {
106
+ const dep = deps [ i ]
107
+ if ( wasTracked ( dep ) && ! newTracked ( dep ) ) {
108
+ dep . delete ( this )
109
+ } else {
110
+ deps [ ptr ++ ] = dep
111
+ }
112
+ resetTracked ( dep )
113
+ }
114
+ deps . length = ptr
115
+ }
116
+ }
117
+
72
118
cleanup ( ) {
73
119
const { deps } = this
74
120
if ( deps . length ) {
@@ -90,6 +136,20 @@ export class ReactiveEffect<T = any> {
90
136
}
91
137
}
92
138
139
+ // The number of effects currently being tracked recursively.
140
+ let effectTrackDepth = 0
141
+
142
+ /**
143
+ * The bitwise track markers support at most 30 levels op recursion.
144
+ * This value is chosen to enable modern JS engines to use a SMI on all platforms.
145
+ * When recursion depth is greater, fall back to using a full cleanup.
146
+ */
147
+ const maxMarkerBits = 30
148
+
149
+ export function getTrackOpBit ( ) : number {
150
+ return 1 << effectTrackDepth
151
+ }
152
+
93
153
export interface ReactiveEffectOptions {
94
154
lazy ?: boolean
95
155
scheduler ?: EffectScheduler
@@ -158,7 +218,8 @@ export function track(target: object, type: TrackOpTypes, key: unknown) {
158
218
}
159
219
let dep = depsMap . get ( key )
160
220
if ( ! dep ) {
161
- depsMap . set ( key , ( dep = new Set ( ) ) )
221
+ dep = createDep ( )
222
+ depsMap . set ( key , dep )
162
223
}
163
224
164
225
const eventInfo = __DEV__
@@ -173,10 +234,21 @@ export function isTracking() {
173
234
}
174
235
175
236
export function trackEffects (
176
- dep : Set < ReactiveEffect > ,
237
+ dep : Dep ,
177
238
debuggerEventExtraInfo ?: DebuggerEventExtraInfo
178
239
) {
179
- if ( ! dep . has ( activeEffect ! ) ) {
240
+ let shouldTrack = false
241
+ if ( effectTrackDepth <= maxMarkerBits ) {
242
+ if ( ! newTracked ( dep ) ) {
243
+ setNewTracked ( dep )
244
+ shouldTrack = ! wasTracked ( dep )
245
+ }
246
+ } else {
247
+ // Full cleanup mode.
248
+ shouldTrack = ! dep . has ( activeEffect ! )
249
+ }
250
+
251
+ if ( shouldTrack ) {
180
252
dep . add ( activeEffect ! )
181
253
activeEffect ! . deps . push ( dep )
182
254
if ( __DEV__ && activeEffect ! . onTrack ) {
@@ -267,7 +339,7 @@ export function trigger(
267
339
effects . push ( ...dep )
268
340
}
269
341
}
270
- triggerEffects ( new Set ( effects ) , eventInfo )
342
+ triggerEffects ( createDep ( effects ) , eventInfo )
271
343
}
272
344
}
273
345
0 commit comments