Skip to content

Commit fcf8bc3

Browse files
authored
fix(watch): align behavior with vue-next(doWatch). (#710)
1 parent ea3ad5c commit fcf8bc3

File tree

2 files changed

+65
-4
lines changed

2 files changed

+65
-4
lines changed

src/apis/watch.ts

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,17 @@
11
import { ComponentInstance } from '../component'
22
import { Ref, isRef, isReactive } from '../reactivity'
3-
import { assert, logError, noopFn, warn, isFunction } from '../utils'
3+
import {
4+
assert,
5+
logError,
6+
noopFn,
7+
warn,
8+
isFunction,
9+
isObject,
10+
isArray,
11+
isPlainObject,
12+
isSet,
13+
isMap,
14+
} from '../utils'
415
import { defineComponentInstance } from '../utils/helper'
516
import { getCurrentInstance, getVueConstructor } from '../runtimeContext'
617
import {
@@ -272,13 +283,29 @@ function createWatcher(
272283
let deep = options.deep
273284

274285
let getter: () => any
275-
if (Array.isArray(source)) {
276-
getter = () => source.map((s) => (isRef(s) ? s.value : s()))
277-
} else if (isRef(source)) {
286+
if (isRef(source)) {
278287
getter = () => source.value
279288
} else if (isReactive(source)) {
280289
getter = () => source
281290
deep = true
291+
} else if (isArray(source)) {
292+
getter = () =>
293+
source.map((s) => {
294+
if (isRef(s)) {
295+
return s.value
296+
} else if (isReactive(s)) {
297+
return traverse(s)
298+
} else if (isFunction(s)) {
299+
return s()
300+
} else {
301+
warn(
302+
`Invalid watch source: ${JSON.stringify(s)}.
303+
A watch source can only be a getter/effect function, a ref, a reactive object, or an array of these types.`,
304+
vm
305+
)
306+
return noopFn
307+
}
308+
})
282309
} else if (isFunction(source)) {
283310
getter = source as () => any
284311
} else {
@@ -405,3 +432,26 @@ export function watch<T = any>(
405432

406433
return createWatcher(vm, source, callback, opts)
407434
}
435+
436+
function traverse(value: unknown, seen: Set<unknown> = new Set()) {
437+
if (!isObject(value) || seen.has(value)) {
438+
return value
439+
}
440+
seen.add(value)
441+
if (isRef(value)) {
442+
traverse(value.value, seen)
443+
} else if (isArray(value)) {
444+
for (let i = 0; i < value.length; i++) {
445+
traverse(value[i], seen)
446+
}
447+
} else if (isSet(value) || isMap(value)) {
448+
value.forEach((v: any) => {
449+
traverse(v, seen)
450+
})
451+
} else if (isPlainObject(value)) {
452+
for (const key in value) {
453+
traverse((value as any)[key], seen)
454+
}
455+
}
456+
return value
457+
}

src/utils/utils.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,17 @@ export function isArray<T>(x: unknown): x is T[] {
6060
return Array.isArray(x)
6161
}
6262

63+
export const objectToString = Object.prototype.toString
64+
65+
export const toTypeString = (value: unknown): string =>
66+
objectToString.call(value)
67+
68+
export const isMap = (val: unknown): val is Map<any, any> =>
69+
toTypeString(val) === '[object Map]'
70+
71+
export const isSet = (val: unknown): val is Set<any> =>
72+
toTypeString(val) === '[object Set]'
73+
6374
export function isValidArrayIndex(val: any): boolean {
6475
const n = parseFloat(String(val))
6576
return n >= 0 && Math.floor(n) === n && isFinite(val)

0 commit comments

Comments
 (0)