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)
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()