@@ -13,7 +13,6 @@ import type { ComputedRef } from './computed'
13
13
import { ReactiveFlags } from './constants'
14
14
import {
15
15
type DebuggerOptions ,
16
- type EffectScheduler ,
17
16
ReactiveEffect ,
18
17
cleanup ,
19
18
onCleanup ,
@@ -119,42 +118,28 @@ export function onWatcherCleanup(
119
118
}
120
119
121
120
class WatcherEffect extends ReactiveEffect {
121
+ forceTrigger : boolean
122
+ isMultiSource : boolean
123
+ oldValue : any
124
+ boundCleanup : typeof onWatcherCleanup
125
+
122
126
constructor (
123
127
source : WatchSource | WatchSource [ ] | WatchEffect | object ,
124
- cb ?: WatchCallback | null ,
128
+ public cb ?: WatchCallback < any , any > | null | undefined ,
125
129
public options : WatchOptions = EMPTY_OBJ ,
126
130
) {
127
- const { immediate, deep, once, scheduler, augmentJob, call } = options
128
-
129
- const warnInvalidSource = ( s : unknown ) => {
130
- ; ( options . onWarn || warn ) (
131
- `Invalid watch source: ` ,
132
- s ,
133
- `A watch source can only be a getter/effect function, a ref, ` +
134
- `a reactive object, or an array of these types.` ,
135
- )
136
- }
137
-
138
- const reactiveGetter = ( source : object ) => {
139
- // traverse will happen in wrapped getter below
140
- if ( deep ) return source
141
- // for `deep: false | 0` or shallow reactive, only traverse root-level properties
142
- if ( isShallow ( source ) || deep === false || deep === 0 )
143
- return traverse ( source , 1 )
144
- // for `deep: undefined` on a reactive object, deeply traverse all properties
145
- return traverse ( source )
146
- }
131
+ const { immediate, deep, once, scheduler, augmentJob, call, onWarn } =
132
+ options
147
133
148
134
let getter : ( ) => any
149
- let boundCleanup : typeof onWatcherCleanup
150
135
let forceTrigger = false
151
136
let isMultiSource = false
152
137
153
138
if ( isRef ( source ) ) {
154
139
getter = ( ) => source . value
155
140
forceTrigger = isShallow ( source )
156
141
} else if ( isReactive ( source ) ) {
157
- getter = ( ) => reactiveGetter ( source )
142
+ getter = ( ) => reactiveGetter ( source , deep )
158
143
forceTrigger = true
159
144
} else if ( isArray ( source ) ) {
160
145
isMultiSource = true
@@ -164,11 +149,11 @@ class WatcherEffect extends ReactiveEffect {
164
149
if ( isRef ( s ) ) {
165
150
return s . value
166
151
} else if ( isReactive ( s ) ) {
167
- return reactiveGetter ( s )
152
+ return reactiveGetter ( s , deep )
168
153
} else if ( isFunction ( s ) ) {
169
154
return call ? call ( s , WatchErrorCodes . WATCH_GETTER ) : s ( )
170
155
} else {
171
- __DEV__ && warnInvalidSource ( s )
156
+ __DEV__ && warnInvalidSource ( s , onWarn )
172
157
}
173
158
} )
174
159
} else if ( isFunction ( source ) ) {
@@ -192,16 +177,18 @@ class WatcherEffect extends ReactiveEffect {
192
177
activeWatcher = this
193
178
try {
194
179
return call
195
- ? call ( source , WatchErrorCodes . WATCH_CALLBACK , [ boundCleanup ] )
196
- : source ( boundCleanup )
180
+ ? call ( source , WatchErrorCodes . WATCH_CALLBACK , [
181
+ this . boundCleanup ,
182
+ ] )
183
+ : source ( this . boundCleanup )
197
184
} finally {
198
185
activeWatcher = currentEffect
199
186
}
200
187
}
201
188
}
202
189
} else {
203
190
getter = NOOP
204
- __DEV__ && warnInvalidSource ( source )
191
+ __DEV__ && warnInvalidSource ( source , onWarn )
205
192
}
206
193
207
194
if ( cb && deep ) {
@@ -211,6 +198,8 @@ class WatcherEffect extends ReactiveEffect {
211
198
}
212
199
213
200
super ( getter )
201
+ this . forceTrigger = forceTrigger
202
+ this . isMultiSource = isMultiSource
214
203
215
204
if ( once && cb ) {
216
205
const _cb = cb
@@ -220,65 +209,23 @@ class WatcherEffect extends ReactiveEffect {
220
209
}
221
210
}
222
211
223
- let oldValue : any = isMultiSource
212
+ this . cb = cb
213
+
214
+ this . oldValue = isMultiSource
224
215
? new Array ( ( source as [ ] ) . length ) . fill ( INITIAL_WATCHER_VALUE )
225
216
: INITIAL_WATCHER_VALUE
226
217
227
- const job = ( ) => {
228
- if ( ! this . dirty ) {
229
- return
230
- }
231
- if ( cb ) {
232
- // watch(source, cb)
233
- const newValue = this . run ( )
234
- if (
235
- deep ||
236
- forceTrigger ||
237
- ( isMultiSource
238
- ? ( newValue as any [ ] ) . some ( ( v , i ) => hasChanged ( v , oldValue [ i ] ) )
239
- : hasChanged ( newValue , oldValue ) )
240
- ) {
241
- // cleanup before running cb again
242
- if ( this . cleanups ) {
243
- cleanup ( this , this . cleanups )
244
- }
245
- const currentWatcher = activeWatcher
246
- activeWatcher = this
247
- try {
248
- const args = [
249
- newValue ,
250
- // pass undefined as the old value when it's changed for the first time
251
- oldValue === INITIAL_WATCHER_VALUE
252
- ? undefined
253
- : isMultiSource && oldValue [ 0 ] === INITIAL_WATCHER_VALUE
254
- ? [ ]
255
- : oldValue ,
256
- boundCleanup ,
257
- ]
258
- call
259
- ? call ( cb ! , WatchErrorCodes . WATCH_CALLBACK , args )
260
- : // @ts -expect-error
261
- cb ! ( ...args )
262
- oldValue = newValue
263
- } finally {
264
- activeWatcher = currentWatcher
265
- }
266
- }
267
- } else {
268
- // watchEffect
269
- this . run ( )
270
- }
271
- }
218
+ const job = this . scheduler . bind ( this )
272
219
273
220
if ( augmentJob ) {
274
221
augmentJob ( job )
275
222
}
276
223
277
- this . scheduler = scheduler
278
- ? ( ) => scheduler ( job , false )
279
- : ( job as EffectScheduler )
224
+ if ( scheduler ) {
225
+ this . scheduler = ( ) => scheduler ( job , false )
226
+ }
280
227
281
- boundCleanup = fn => onWatcherCleanup ( fn , false , this )
228
+ this . boundCleanup = fn => onWatcherCleanup ( fn , false , this )
282
229
283
230
if ( __DEV__ ) {
284
231
this . onTrack = options . onTrack
@@ -290,14 +237,80 @@ class WatcherEffect extends ReactiveEffect {
290
237
if ( immediate ) {
291
238
job ( )
292
239
} else {
293
- oldValue = this . run ( )
240
+ this . oldValue = this . run ( )
294
241
}
295
242
} else if ( scheduler ) {
296
- scheduler ( job . bind ( null ) , true )
243
+ scheduler ( job , true )
297
244
} else {
298
245
this . run ( )
299
246
}
300
247
}
248
+
249
+ scheduler ( ) : void {
250
+ if ( ! this . dirty ) {
251
+ return
252
+ }
253
+ if ( this . cb ) {
254
+ // watch(source, cb)
255
+ const newValue = this . run ( )
256
+ const { deep, call } = this . options
257
+ if (
258
+ deep ||
259
+ this . forceTrigger ||
260
+ ( this . isMultiSource
261
+ ? ( newValue as any [ ] ) . some ( ( v , i ) => hasChanged ( v , this . oldValue [ i ] ) )
262
+ : hasChanged ( newValue , this . oldValue ) )
263
+ ) {
264
+ // cleanup before running cb again
265
+ if ( this . cleanups ) {
266
+ cleanup ( this , this . cleanups )
267
+ }
268
+ const currentWatcher = activeWatcher
269
+ activeWatcher = this
270
+ try {
271
+ const args = [
272
+ newValue ,
273
+ // pass undefined as the old value when it's changed for the first time
274
+ this . oldValue === INITIAL_WATCHER_VALUE
275
+ ? undefined
276
+ : this . isMultiSource && this . oldValue [ 0 ] === INITIAL_WATCHER_VALUE
277
+ ? [ ]
278
+ : this . oldValue ,
279
+ this . boundCleanup ,
280
+ ]
281
+ call
282
+ ? call ( this . cb , WatchErrorCodes . WATCH_CALLBACK , args )
283
+ : // @ts -expect-error
284
+ this . cb ( ...args )
285
+ this . oldValue = newValue
286
+ } finally {
287
+ activeWatcher = currentWatcher
288
+ }
289
+ }
290
+ } else {
291
+ // watchEffect
292
+ this . run ( )
293
+ }
294
+ }
295
+ }
296
+
297
+ function reactiveGetter ( source : object , deep : WatchOptions [ 'deep' ] ) : unknown {
298
+ // traverse will happen in wrapped getter below
299
+ if ( deep ) return source
300
+ // for `deep: false | 0` or shallow reactive, only traverse root-level properties
301
+ if ( isShallow ( source ) || deep === false || deep === 0 )
302
+ return traverse ( source , 1 )
303
+ // for `deep: undefined` on a reactive object, deeply traverse all properties
304
+ return traverse ( source )
305
+ }
306
+
307
+ function warnInvalidSource ( s : object , onWarn : WatchOptions [ 'onWarn' ] ) : void {
308
+ ; ( onWarn || warn ) (
309
+ `Invalid watch source: ` ,
310
+ s ,
311
+ `A watch source can only be a getter/effect function, a ref, ` +
312
+ `a reactive object, or an array of these types.` ,
313
+ )
301
314
}
302
315
303
316
export function watch (
0 commit comments