diff --git a/packages/reactivity/src/watch.ts b/packages/reactivity/src/watch.ts index 648e6481b18..6bc009985e0 100644 --- a/packages/reactivity/src/watch.ts +++ b/packages/reactivity/src/watch.ts @@ -331,17 +331,17 @@ export function watch( export function traverse( value: unknown, depth: number = Infinity, - seen?: Set, + seen?: Map, ): 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) diff --git a/packages/runtime-core/__tests__/apiWatch.spec.ts b/packages/runtime-core/__tests__/apiWatch.spec.ts index ff06fbea774..07a589fb0f5 100644 --- a/packages/runtime-core/__tests__/apiWatch.spec.ts +++ b/packages/runtime-core/__tests__/apiWatch.spec.ts @@ -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()