mirror of https://github.com/vuejs/core.git
fix(runtime-core): support deep: false when watch reactive (#9928)
close #9916 --------- Co-authored-by: RicardoErii <‘1974364190@qq.com’> Co-authored-by: Evan You <yyx990803@gmail.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
parent
dce99c12df
commit
4f703d120d
|
@ -25,9 +25,11 @@ import {
|
|||
type DebuggerEvent,
|
||||
ITERATE_KEY,
|
||||
type Ref,
|
||||
type ShallowRef,
|
||||
TrackOpTypes,
|
||||
TriggerOpTypes,
|
||||
effectScope,
|
||||
shallowReactive,
|
||||
shallowRef,
|
||||
toRef,
|
||||
triggerRef,
|
||||
|
@ -156,6 +158,59 @@ describe('api: watch', () => {
|
|||
expect(dummy).toBe(1)
|
||||
})
|
||||
|
||||
it('directly watching reactive object with explicit deep: false', async () => {
|
||||
const src = reactive({
|
||||
state: {
|
||||
count: 0,
|
||||
},
|
||||
})
|
||||
let dummy
|
||||
watch(
|
||||
src,
|
||||
({ state }) => {
|
||||
dummy = state?.count
|
||||
},
|
||||
{
|
||||
deep: false,
|
||||
},
|
||||
)
|
||||
|
||||
// nested should not trigger
|
||||
src.state.count++
|
||||
await nextTick()
|
||||
expect(dummy).toBe(undefined)
|
||||
|
||||
// root level should trigger
|
||||
src.state = { count: 1 }
|
||||
await nextTick()
|
||||
expect(dummy).toBe(1)
|
||||
})
|
||||
|
||||
// #9916
|
||||
it('directly watching shallow reactive array', async () => {
|
||||
class foo {
|
||||
prop1: ShallowRef<string> = shallowRef('')
|
||||
prop2: string = ''
|
||||
}
|
||||
|
||||
const obj1 = new foo()
|
||||
const obj2 = new foo()
|
||||
|
||||
const collection = shallowReactive([obj1, obj2])
|
||||
const cb = vi.fn()
|
||||
watch(collection, cb)
|
||||
|
||||
collection[0].prop1.value = 'foo'
|
||||
await nextTick()
|
||||
// should not trigger
|
||||
expect(cb).toBeCalledTimes(0)
|
||||
|
||||
collection.push(new foo())
|
||||
await nextTick()
|
||||
// should trigger on array self mutation
|
||||
expect(cb).toBeCalledTimes(1)
|
||||
})
|
||||
|
||||
it('watching multiple sources', async () => {
|
||||
const state = reactive({ count: 1 })
|
||||
const count = ref(1)
|
||||
|
|
|
@ -231,8 +231,11 @@ function doWatch(
|
|||
getter = () => source.value
|
||||
forceTrigger = isShallow(source)
|
||||
} else if (isReactive(source)) {
|
||||
getter = () => source
|
||||
deep = true
|
||||
getter =
|
||||
isShallow(source) || deep === false
|
||||
? () => traverse(source, 1)
|
||||
: () => traverse(source)
|
||||
forceTrigger = true
|
||||
} else if (isArray(source)) {
|
||||
isMultiSource = true
|
||||
forceTrigger = source.some(s => isReactive(s) || isShallow(s))
|
||||
|
@ -241,7 +244,7 @@ function doWatch(
|
|||
if (isRef(s)) {
|
||||
return s.value
|
||||
} else if (isReactive(s)) {
|
||||
return traverse(s)
|
||||
return traverse(s, isShallow(s) || deep === false ? 1 : undefined)
|
||||
} else if (isFunction(s)) {
|
||||
return callWithErrorHandling(s, instance, ErrorCodes.WATCH_GETTER)
|
||||
} else {
|
||||
|
@ -460,28 +463,41 @@ export function createPathGetter(ctx: any, path: string) {
|
|||
}
|
||||
}
|
||||
|
||||
export function traverse(value: unknown, seen?: Set<unknown>) {
|
||||
export function traverse(
|
||||
value: unknown,
|
||||
depth?: number,
|
||||
currentDepth = 0,
|
||||
seen?: Set<unknown>,
|
||||
) {
|
||||
if (!isObject(value) || (value as any)[ReactiveFlags.SKIP]) {
|
||||
return value
|
||||
}
|
||||
|
||||
if (depth && depth > 0) {
|
||||
if (currentDepth >= depth) {
|
||||
return value
|
||||
}
|
||||
currentDepth++
|
||||
}
|
||||
|
||||
seen = seen || new Set()
|
||||
if (seen.has(value)) {
|
||||
return value
|
||||
}
|
||||
seen.add(value)
|
||||
if (isRef(value)) {
|
||||
traverse(value.value, seen)
|
||||
traverse(value.value, depth, currentDepth, seen)
|
||||
} else if (isArray(value)) {
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
traverse(value[i], seen)
|
||||
traverse(value[i], depth, currentDepth, seen)
|
||||
}
|
||||
} else if (isSet(value) || isMap(value)) {
|
||||
value.forEach((v: any) => {
|
||||
traverse(v, seen)
|
||||
traverse(v, depth, currentDepth, seen)
|
||||
})
|
||||
} else if (isPlainObject(value)) {
|
||||
for (const key in value) {
|
||||
traverse(value[key], seen)
|
||||
traverse(value[key], depth, currentDepth, seen)
|
||||
}
|
||||
}
|
||||
return value
|
||||
|
|
Loading…
Reference in New Issue