type DebuggerEvent,
ITERATE_KEY,
type Ref,
+ type ShallowRef,
TrackOpTypes,
TriggerOpTypes,
effectScope,
+ shallowReactive,
shallowRef,
toRef,
triggerRef,
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)
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))
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 {
}
}
-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