Skip to content

Commit c9136b1

Browse files
committed
wip: apiWatch tests
1 parent 6c1d2ef commit c9136b1

File tree

9 files changed

+217
-259
lines changed

9 files changed

+217
-259
lines changed

src/core/instance/lifecycle.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,12 @@ export function mountComponent(
218218
// manually mounted instance, call mounted on self
219219
// mounted is called for render-created child components in its inserted hook
220220
if (vm.$vnode == null) {
221+
const preWatchers = vm._preWatchers
222+
if (preWatchers) {
223+
for (let i = 0; i < preWatchers.length; i++) {
224+
preWatchers[i].run()
225+
}
226+
}
221227
vm._isMounted = true
222228
callHook(vm, 'mounted')
223229
}

src/core/observer/scheduler.ts

Lines changed: 29 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type Watcher from './watcher'
22
import config from '../config'
3+
import Dep from './dep'
34
import { callHook, activateChildComponent } from '../instance/lifecycle'
45

56
import { warn, nextTick, devtools, inBrowser, isIE } from '../util/index'
@@ -119,12 +120,12 @@ function flushSchedulerQueue() {
119120
}
120121
}
121122

122-
function callUpdatedHooks(queue) {
123+
function callUpdatedHooks(queue: Watcher[]) {
123124
let i = queue.length
124125
while (i--) {
125126
const watcher = queue[i]
126127
const vm = watcher.vm
127-
if (vm._watcher === watcher && vm._isMounted && !vm._isDestroyed) {
128+
if (vm && vm._watcher === watcher && vm._isMounted && !vm._isDestroyed) {
128129
callHook(vm, 'updated')
129130
}
130131
}
@@ -155,28 +156,34 @@ function callActivatedHooks(queue) {
155156
*/
156157
export function queueWatcher(watcher: Watcher) {
157158
const id = watcher.id
158-
if (has[id] == null) {
159-
has[id] = true
160-
if (!flushing) {
161-
queue.push(watcher)
162-
} else {
163-
// if already flushing, splice the watcher based on its id
164-
// if already past its id, it will be run next immediately.
165-
let i = queue.length - 1
166-
while (i > index && queue[i].id > watcher.id) {
167-
i--
168-
}
169-
queue.splice(i + 1, 0, watcher)
159+
if (has[id] != null) {
160+
return
161+
}
162+
163+
if (watcher === Dep.target && watcher.noRecurse) {
164+
return
165+
}
166+
167+
has[id] = true
168+
if (!flushing) {
169+
queue.push(watcher)
170+
} else {
171+
// if already flushing, splice the watcher based on its id
172+
// if already past its id, it will be run next immediately.
173+
let i = queue.length - 1
174+
while (i > index && queue[i].id > watcher.id) {
175+
i--
170176
}
171-
// queue the flush
172-
if (!waiting) {
173-
waiting = true
177+
queue.splice(i + 1, 0, watcher)
178+
}
179+
// queue the flush
180+
if (!waiting) {
181+
waiting = true
174182

175-
if (__DEV__ && !config.async) {
176-
flushSchedulerQueue()
177-
return
178-
}
179-
nextTick(flushSchedulerQueue)
183+
if (__DEV__ && !config.async) {
184+
flushSchedulerQueue()
185+
return
180186
}
187+
nextTick(flushSchedulerQueue)
181188
}
182189
}

src/core/observer/traverse.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ const seenObjects = new Set()
1212
export function traverse(val: any) {
1313
_traverse(val, seenObjects)
1414
seenObjects.clear()
15+
return val
1516
}
1617

1718
function _traverse(val: any, seen: SimpleSet) {

src/core/observer/watcher.ts

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import {
77
handleError,
88
invokeWithErrorHandling,
99
noop,
10-
bind,
1110
isFunction
1211
} from '../util/index'
1312

@@ -26,7 +25,7 @@ let uid = 0
2625
* This is used for both the $watch() api and directives.
2726
*/
2827
export default class Watcher implements DepTarget {
29-
vm: Component | null
28+
vm?: Component | null
3029
expression: string
3130
cb: Function
3231
id: number
@@ -41,7 +40,8 @@ export default class Watcher implements DepTarget {
4140
depIds: SimpleSet
4241
newDepIds: SimpleSet
4342
before?: Function
44-
scheduler?: Function
43+
onStop?: Function
44+
noRecurse?: boolean
4545
getter: Function
4646
value: any
4747

@@ -72,10 +72,6 @@ export default class Watcher implements DepTarget {
7272
this.lazy = !!options.lazy
7373
this.sync = !!options.sync
7474
this.before = options.before
75-
if ((this.scheduler = options.scheduler)) {
76-
// @ts-ignore
77-
this.run = bind(this.run, this)
78-
}
7975
} else {
8076
this.deep = this.user = this.lazy = this.sync = false
8177
}
@@ -179,8 +175,6 @@ export default class Watcher implements DepTarget {
179175
this.dirty = true
180176
} else if (this.sync) {
181177
this.run()
182-
} else if (this.scheduler) {
183-
this.scheduler(this.run)
184178
} else {
185179
queueWatcher(this)
186180
}
@@ -255,6 +249,9 @@ export default class Watcher implements DepTarget {
255249
this.deps[i].removeSub(this)
256250
}
257251
this.active = false
252+
if (this.onStop) {
253+
this.onStop()
254+
}
258255
}
259256
}
260257
}

src/v3/apiWatch.ts

Lines changed: 70 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,15 @@ import {
77
isArray,
88
isFunction,
99
emptyObject,
10-
remove,
1110
hasChanged,
1211
isServerRendering,
1312
invokeWithErrorHandling
1413
} from 'core/util'
1514
import { currentInstance } from './currentInstance'
1615
import { traverse } from 'core/observer/traverse'
17-
import {
18-
EffectScheduler,
19-
ReactiveEffect,
20-
DebuggerEvent
21-
} from './reactivity/effect'
16+
import Watcher from '../core/observer/watcher'
17+
import { queueWatcher } from '../core/observer/scheduler'
18+
import { TrackOpTypes, TriggerOpTypes } from './reactivity/operations'
2219

2320
const WATCHER = `watcher`
2421
const WATCHER_CB = `${WATCHER} callback`
@@ -58,6 +55,19 @@ export interface DebuggerOptions {
5855
onTrigger?: (event: DebuggerEvent) => void
5956
}
6057

58+
export type DebuggerEvent = {
59+
watcher: Watcher
60+
} & DebuggerEventExtraInfo
61+
62+
export type DebuggerEventExtraInfo = {
63+
target: object
64+
type: TrackOpTypes | TriggerOpTypes
65+
key: any
66+
newValue?: any
67+
oldValue?: any
68+
oldTarget?: Map<any, any> | Set<any>
69+
}
70+
6171
export interface WatchOptions<Immediate = boolean> extends WatchOptionsBase {
6272
immediate?: Immediate
6373
deep?: boolean
@@ -162,7 +172,13 @@ export function watch<T = any, Immediate extends Readonly<boolean> = false>(
162172
function doWatch(
163173
source: WatchSource | WatchSource[] | WatchEffect | object,
164174
cb: WatchCallback | null,
165-
{ immediate, deep, flush, onTrack, onTrigger }: WatchOptions = emptyObject
175+
{
176+
immediate,
177+
deep,
178+
flush = 'pre',
179+
onTrack,
180+
onTrigger
181+
}: WatchOptions = emptyObject
166182
): WatchStopHandle {
167183
if (__DEV__ && !cb) {
168184
if (immediate !== undefined) {
@@ -200,7 +216,12 @@ function doWatch(
200216
getter = () => source.value
201217
forceTrigger = isShallow(source)
202218
} else if (isReactive(source)) {
203-
getter = () => source
219+
getter = isArray(source)
220+
? () => {
221+
;(source as any).__ob__.dep.depend()
222+
return source
223+
}
224+
: () => source
204225
deep = true
205226
} else if (isArray(source)) {
206227
isMultiSource = true
@@ -245,7 +266,7 @@ function doWatch(
245266

246267
let cleanup: () => void
247268
let onCleanup: OnCleanup = (fn: () => void) => {
248-
cleanup = effect.onStop = () => {
269+
cleanup = watcher.onStop = () => {
249270
call(fn, WATCHER_CLEANUP)
250271
}
251272
}
@@ -267,14 +288,23 @@ function doWatch(
267288
return noop
268289
}
269290

291+
const watcher = new Watcher(currentInstance, getter, noop, {
292+
lazy: true
293+
})
294+
watcher.noRecurse = !cb
295+
270296
let oldValue = isMultiSource ? [] : INITIAL_WATCHER_VALUE
271-
const job = () => {
272-
if (!effect.active) {
297+
// overwrite default run
298+
watcher.run = () => {
299+
if (
300+
!watcher.active &&
301+
!(flush === 'pre' && instance && instance._isBeingDestroyed)
302+
) {
273303
return
274304
}
275305
if (cb) {
276306
// watch(source, cb)
277-
const newValue = effect.run()
307+
const newValue = watcher.get()
278308
if (
279309
deep ||
280310
forceTrigger ||
@@ -298,52 +328,51 @@ function doWatch(
298328
}
299329
} else {
300330
// watchEffect
301-
effect.run()
331+
watcher.get()
302332
}
303333
}
304334

305-
let scheduler: EffectScheduler
306335
if (flush === 'sync') {
307-
scheduler = job as any // the scheduler function gets called directly
336+
watcher.update = watcher.run
308337
} else if (flush === 'post') {
309-
scheduler = () => queuePostRenderEffect(job)
338+
watcher.id = Infinity
339+
watcher.update = () => queueWatcher(watcher)
310340
} else {
311-
// default: 'pre'
312-
scheduler = () => queuePreFlushCb(job)
341+
// pre
342+
watcher.update = () => {
343+
if (!instance || instance._isMounted) {
344+
queueWatcher(watcher)
345+
} else {
346+
const buffer = instance._preWatchers || (instance._preWatchers = [])
347+
if (buffer.indexOf(watcher) < 0) buffer.push(watcher)
348+
}
349+
}
313350
}
314351

315-
const effect = new ReactiveEffect(getter, scheduler)
316-
317-
if (__DEV__) {
318-
effect.onTrack = onTrack
319-
effect.onTrigger = onTrigger
320-
}
352+
// TODO
353+
// if (__DEV__) {
354+
// effect.onTrack = onTrack
355+
// effect.onTrigger = onTrigger
356+
// }
321357

322358
// initial run
323359
if (cb) {
324360
if (immediate) {
325-
job()
361+
watcher.run()
326362
} else {
327-
oldValue = effect.run()
363+
oldValue = watcher.get()
328364
}
329-
} else if (flush === 'post') {
330-
queuePostRenderEffect(effect.run.bind(effect))
365+
} else if (flush === 'post' && instance) {
366+
instance.$once('hook:mounted', () => watcher.get())
331367
} else {
332-
effect.run()
368+
watcher.get()
333369
}
334370

335371
return () => {
336-
effect.stop()
337-
if (instance && instance.scope) {
338-
remove(instance.scope.effects!, effect)
339-
}
372+
watcher.teardown()
373+
// TODO
374+
// if (instance && instance.scope) {
375+
// remove(instance.scope.effects!, effect)
376+
// }
340377
}
341378
}
342-
343-
function queuePostRenderEffect(fn: Function) {
344-
// TODO
345-
}
346-
347-
function queuePreFlushCb(fn: Function) {
348-
// TODO
349-
}

src/v3/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,10 @@ export {
5353
WatchCallback,
5454
WatchSource,
5555
WatchStopHandle,
56-
DebuggerOptions
56+
DebuggerOptions,
57+
DebuggerEvent
5758
} from './apiWatch'
5859

59-
export { DebuggerEvent } from './reactivity/effect'
6060
export { TrackOpTypes, TriggerOpTypes } from './reactivity/operations'
6161

6262
export { h } from './h'

0 commit comments

Comments
 (0)