fix(watch): use maximum depth for duplicates (#13434)

This commit is contained in:
skirtle 2025-09-02 10:29:08 +01:00 committed by GitHub
parent 99d54b28b4
commit f2699a5cb3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 55 additions and 4 deletions

View File

@ -331,17 +331,17 @@ export function watch(
export function traverse(
value: unknown,
depth: number = Infinity,
seen?: Set<unknown>,
seen?: Map<unknown, number>,
): unknown {
if (depth <= 0 || !isObject(value) || (value as any)[ReactiveFlags.SKIP]) {
return value
}
seen = seen || new Set()
if (seen.has(value)) {
seen = seen || new Map()
if ((seen.get(value) || 0) >= depth) {
return value
}
seen.add(value)
seen.set(value, depth)
depth--
if (isRef(value)) {
traverse(value.value, depth, seen)

View File

@ -1689,6 +1689,57 @@ describe('api: watch', () => {
expect(cb).toHaveBeenCalledTimes(4)
})
test('watching the same object at different depths', async () => {
const arr1: any[] = reactive([[[{ foo: {} }]]])
const arr2 = arr1[0]
const arr3 = arr2[0]
const obj = arr3[0]
arr1.push(arr3)
const cb1 = vi.fn()
const cb2 = vi.fn()
const cb3 = vi.fn()
const cb4 = vi.fn()
watch(arr1, cb1, { deep: 1 })
watch(arr1, cb2, { deep: 2 })
watch(arr1, cb3, { deep: 3 })
watch(arr1, cb4, { deep: 4 })
await nextTick()
expect(cb1).toHaveBeenCalledTimes(0)
expect(cb2).toHaveBeenCalledTimes(0)
expect(cb3).toHaveBeenCalledTimes(0)
expect(cb4).toHaveBeenCalledTimes(0)
obj.foo = {}
await nextTick()
expect(cb1).toHaveBeenCalledTimes(0)
expect(cb2).toHaveBeenCalledTimes(0)
expect(cb3).toHaveBeenCalledTimes(1)
expect(cb4).toHaveBeenCalledTimes(1)
obj.foo.bar = 1
await nextTick()
expect(cb1).toHaveBeenCalledTimes(0)
expect(cb2).toHaveBeenCalledTimes(0)
expect(cb3).toHaveBeenCalledTimes(1)
expect(cb4).toHaveBeenCalledTimes(2)
arr3.push(obj.foo)
await nextTick()
expect(cb1).toHaveBeenCalledTimes(0)
expect(cb2).toHaveBeenCalledTimes(1)
expect(cb3).toHaveBeenCalledTimes(2)
expect(cb4).toHaveBeenCalledTimes(3)
obj.foo.bar = 2
await nextTick()
expect(cb1).toHaveBeenCalledTimes(0)
expect(cb2).toHaveBeenCalledTimes(1)
expect(cb3).toHaveBeenCalledTimes(3)
expect(cb4).toHaveBeenCalledTimes(4)
})
test('pause / resume', async () => {
const count = ref(0)
const cb = vi.fn()