Skip to content

Commit 57d428c

Browse files
committed
wip
1 parent dc438a8 commit 57d428c

File tree

1 file changed

+94
-81
lines changed

1 file changed

+94
-81
lines changed

Diff for: packages/reactivity/src/watch.ts

+94-81
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import type { ComputedRef } from './computed'
1313
import { ReactiveFlags } from './constants'
1414
import {
1515
type DebuggerOptions,
16-
type EffectScheduler,
1716
ReactiveEffect,
1817
cleanup,
1918
onCleanup,
@@ -119,42 +118,28 @@ export function onWatcherCleanup(
119118
}
120119

121120
class WatcherEffect extends ReactiveEffect {
121+
forceTrigger: boolean
122+
isMultiSource: boolean
123+
oldValue: any
124+
boundCleanup: typeof onWatcherCleanup
125+
122126
constructor(
123127
source: WatchSource | WatchSource[] | WatchEffect | object,
124-
cb?: WatchCallback | null,
128+
public cb?: WatchCallback<any, any> | null | undefined,
125129
public options: WatchOptions = EMPTY_OBJ,
126130
) {
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
147133

148134
let getter: () => any
149-
let boundCleanup: typeof onWatcherCleanup
150135
let forceTrigger = false
151136
let isMultiSource = false
152137

153138
if (isRef(source)) {
154139
getter = () => source.value
155140
forceTrigger = isShallow(source)
156141
} else if (isReactive(source)) {
157-
getter = () => reactiveGetter(source)
142+
getter = () => reactiveGetter(source, deep)
158143
forceTrigger = true
159144
} else if (isArray(source)) {
160145
isMultiSource = true
@@ -164,11 +149,11 @@ class WatcherEffect extends ReactiveEffect {
164149
if (isRef(s)) {
165150
return s.value
166151
} else if (isReactive(s)) {
167-
return reactiveGetter(s)
152+
return reactiveGetter(s, deep)
168153
} else if (isFunction(s)) {
169154
return call ? call(s, WatchErrorCodes.WATCH_GETTER) : s()
170155
} else {
171-
__DEV__ && warnInvalidSource(s)
156+
__DEV__ && warnInvalidSource(s, onWarn)
172157
}
173158
})
174159
} else if (isFunction(source)) {
@@ -192,16 +177,18 @@ class WatcherEffect extends ReactiveEffect {
192177
activeWatcher = this
193178
try {
194179
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)
197184
} finally {
198185
activeWatcher = currentEffect
199186
}
200187
}
201188
}
202189
} else {
203190
getter = NOOP
204-
__DEV__ && warnInvalidSource(source)
191+
__DEV__ && warnInvalidSource(source, onWarn)
205192
}
206193

207194
if (cb && deep) {
@@ -211,6 +198,8 @@ class WatcherEffect extends ReactiveEffect {
211198
}
212199

213200
super(getter)
201+
this.forceTrigger = forceTrigger
202+
this.isMultiSource = isMultiSource
214203

215204
if (once && cb) {
216205
const _cb = cb
@@ -220,65 +209,23 @@ class WatcherEffect extends ReactiveEffect {
220209
}
221210
}
222211

223-
let oldValue: any = isMultiSource
212+
this.cb = cb
213+
214+
this.oldValue = isMultiSource
224215
? new Array((source as []).length).fill(INITIAL_WATCHER_VALUE)
225216
: INITIAL_WATCHER_VALUE
226217

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)
272219

273220
if (augmentJob) {
274221
augmentJob(job)
275222
}
276223

277-
this.scheduler = scheduler
278-
? () => scheduler(job, false)
279-
: (job as EffectScheduler)
224+
if (scheduler) {
225+
this.scheduler = () => scheduler(job, false)
226+
}
280227

281-
boundCleanup = fn => onWatcherCleanup(fn, false, this)
228+
this.boundCleanup = fn => onWatcherCleanup(fn, false, this)
282229

283230
if (__DEV__) {
284231
this.onTrack = options.onTrack
@@ -290,14 +237,80 @@ class WatcherEffect extends ReactiveEffect {
290237
if (immediate) {
291238
job()
292239
} else {
293-
oldValue = this.run()
240+
this.oldValue = this.run()
294241
}
295242
} else if (scheduler) {
296-
scheduler(job.bind(null), true)
243+
scheduler(job, true)
297244
} else {
298245
this.run()
299246
}
300247
}
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+
)
301314
}
302315

303316
export function watch(

0 commit comments

Comments
 (0)