Skip to content

Commit 4f703d1

Browse files
yangxiuxiu1115RicardoEriiyyx990803autofix-ci[bot]
authored
fix(runtime-core): support deep: false when watch reactive (#9928)
close #9916 --------- Co-authored-by: RicardoErii <‘[email protected]’> Co-authored-by: Evan You <[email protected]> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
1 parent dce99c1 commit 4f703d1

File tree

2 files changed

+79
-8
lines changed

2 files changed

+79
-8
lines changed

packages/runtime-core/__tests__/apiWatch.spec.ts

+55
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,11 @@ import {
2525
type DebuggerEvent,
2626
ITERATE_KEY,
2727
type Ref,
28+
type ShallowRef,
2829
TrackOpTypes,
2930
TriggerOpTypes,
3031
effectScope,
32+
shallowReactive,
3133
shallowRef,
3234
toRef,
3335
triggerRef,
@@ -156,6 +158,59 @@ describe('api: watch', () => {
156158
expect(dummy).toBe(1)
157159
})
158160

161+
it('directly watching reactive object with explicit deep: false', async () => {
162+
const src = reactive({
163+
state: {
164+
count: 0,
165+
},
166+
})
167+
let dummy
168+
watch(
169+
src,
170+
({ state }) => {
171+
dummy = state?.count
172+
},
173+
{
174+
deep: false,
175+
},
176+
)
177+
178+
// nested should not trigger
179+
src.state.count++
180+
await nextTick()
181+
expect(dummy).toBe(undefined)
182+
183+
// root level should trigger
184+
src.state = { count: 1 }
185+
await nextTick()
186+
expect(dummy).toBe(1)
187+
})
188+
189+
// #9916
190+
it('directly watching shallow reactive array', async () => {
191+
class foo {
192+
prop1: ShallowRef<string> = shallowRef('')
193+
prop2: string = ''
194+
}
195+
196+
const obj1 = new foo()
197+
const obj2 = new foo()
198+
199+
const collection = shallowReactive([obj1, obj2])
200+
const cb = vi.fn()
201+
watch(collection, cb)
202+
203+
collection[0].prop1.value = 'foo'
204+
await nextTick()
205+
// should not trigger
206+
expect(cb).toBeCalledTimes(0)
207+
208+
collection.push(new foo())
209+
await nextTick()
210+
// should trigger on array self mutation
211+
expect(cb).toBeCalledTimes(1)
212+
})
213+
159214
it('watching multiple sources', async () => {
160215
const state = reactive({ count: 1 })
161216
const count = ref(1)

packages/runtime-core/src/apiWatch.ts

+24-8
Original file line numberDiff line numberDiff line change
@@ -231,8 +231,11 @@ function doWatch(
231231
getter = () => source.value
232232
forceTrigger = isShallow(source)
233233
} else if (isReactive(source)) {
234-
getter = () => source
235-
deep = true
234+
getter =
235+
isShallow(source) || deep === false
236+
? () => traverse(source, 1)
237+
: () => traverse(source)
238+
forceTrigger = true
236239
} else if (isArray(source)) {
237240
isMultiSource = true
238241
forceTrigger = source.some(s => isReactive(s) || isShallow(s))
@@ -241,7 +244,7 @@ function doWatch(
241244
if (isRef(s)) {
242245
return s.value
243246
} else if (isReactive(s)) {
244-
return traverse(s)
247+
return traverse(s, isShallow(s) || deep === false ? 1 : undefined)
245248
} else if (isFunction(s)) {
246249
return callWithErrorHandling(s, instance, ErrorCodes.WATCH_GETTER)
247250
} else {
@@ -460,28 +463,41 @@ export function createPathGetter(ctx: any, path: string) {
460463
}
461464
}
462465

463-
export function traverse(value: unknown, seen?: Set<unknown>) {
466+
export function traverse(
467+
value: unknown,
468+
depth?: number,
469+
currentDepth = 0,
470+
seen?: Set<unknown>,
471+
) {
464472
if (!isObject(value) || (value as any)[ReactiveFlags.SKIP]) {
465473
return value
466474
}
475+
476+
if (depth && depth > 0) {
477+
if (currentDepth >= depth) {
478+
return value
479+
}
480+
currentDepth++
481+
}
482+
467483
seen = seen || new Set()
468484
if (seen.has(value)) {
469485
return value
470486
}
471487
seen.add(value)
472488
if (isRef(value)) {
473-
traverse(value.value, seen)
489+
traverse(value.value, depth, currentDepth, seen)
474490
} else if (isArray(value)) {
475491
for (let i = 0; i < value.length; i++) {
476-
traverse(value[i], seen)
492+
traverse(value[i], depth, currentDepth, seen)
477493
}
478494
} else if (isSet(value) || isMap(value)) {
479495
value.forEach((v: any) => {
480-
traverse(v, seen)
496+
traverse(v, depth, currentDepth, seen)
481497
})
482498
} else if (isPlainObject(value)) {
483499
for (const key in value) {
484-
traverse(value[key], seen)
500+
traverse(value[key], depth, currentDepth, seen)
485501
}
486502
}
487503
return value

0 commit comments

Comments
 (0)