mirror of https://github.com/vuejs/core.git
feat(watch): support passing number to `deep` option to control the watch depth (#9572)
This commit is contained in:
parent
321d80758c
commit
22f7d96757
|
@ -1532,6 +1532,147 @@ describe('api: watch', () => {
|
||||||
expect(spy2).toHaveBeenCalledTimes(1)
|
expect(spy2).toHaveBeenCalledTimes(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('watching reactive depth', async () => {
|
||||||
|
const state = reactive({
|
||||||
|
a: {
|
||||||
|
b: {
|
||||||
|
c: {
|
||||||
|
d: {
|
||||||
|
e: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const cb = vi.fn()
|
||||||
|
|
||||||
|
watch(state, cb, { deep: 2 })
|
||||||
|
|
||||||
|
state.a.b = { c: { d: { e: 2 } } }
|
||||||
|
await nextTick()
|
||||||
|
expect(cb).toHaveBeenCalledTimes(1)
|
||||||
|
|
||||||
|
state.a.b.c = { d: { e: 3 } }
|
||||||
|
|
||||||
|
await nextTick()
|
||||||
|
expect(cb).toHaveBeenCalledTimes(1)
|
||||||
|
|
||||||
|
state.a.b = { c: { d: { e: 4 } } }
|
||||||
|
|
||||||
|
await nextTick()
|
||||||
|
expect(cb).toHaveBeenCalledTimes(2)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('watching ref depth', async () => {
|
||||||
|
const state = ref({
|
||||||
|
a: {
|
||||||
|
b: 2,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const cb = vi.fn()
|
||||||
|
|
||||||
|
watch(state, cb, { deep: 1 })
|
||||||
|
|
||||||
|
state.value.a.b = 3
|
||||||
|
await nextTick()
|
||||||
|
expect(cb).toHaveBeenCalledTimes(0)
|
||||||
|
|
||||||
|
state.value.a = { b: 3 }
|
||||||
|
await nextTick()
|
||||||
|
expect(cb).toHaveBeenCalledTimes(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('watching array depth', async () => {
|
||||||
|
const arr = ref([
|
||||||
|
{
|
||||||
|
a: {
|
||||||
|
b: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
a: {
|
||||||
|
b: 3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
])
|
||||||
|
const cb = vi.fn()
|
||||||
|
watch(arr, cb, { deep: 2 })
|
||||||
|
|
||||||
|
arr.value[0].a.b = 3
|
||||||
|
await nextTick()
|
||||||
|
expect(cb).toHaveBeenCalledTimes(0)
|
||||||
|
|
||||||
|
arr.value[0].a = { b: 3 }
|
||||||
|
await nextTick()
|
||||||
|
expect(cb).toHaveBeenCalledTimes(1)
|
||||||
|
|
||||||
|
arr.value[1].a = { b: 4 }
|
||||||
|
await nextTick()
|
||||||
|
expect(cb).toHaveBeenCalledTimes(2)
|
||||||
|
|
||||||
|
arr.value.push({ a: { b: 5 } })
|
||||||
|
await nextTick()
|
||||||
|
expect(cb).toHaveBeenCalledTimes(3)
|
||||||
|
|
||||||
|
arr.value.pop()
|
||||||
|
await nextTick()
|
||||||
|
expect(cb).toHaveBeenCalledTimes(4)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('shallowReactive', async () => {
|
||||||
|
const state = shallowReactive({
|
||||||
|
msg: ref('hello'),
|
||||||
|
foo: {
|
||||||
|
a: ref(1),
|
||||||
|
b: 2,
|
||||||
|
},
|
||||||
|
bar: 'bar',
|
||||||
|
})
|
||||||
|
|
||||||
|
const spy = vi.fn()
|
||||||
|
|
||||||
|
watch(state, spy)
|
||||||
|
|
||||||
|
state.msg.value = 'hi'
|
||||||
|
await nextTick()
|
||||||
|
expect(spy).not.toHaveBeenCalled()
|
||||||
|
|
||||||
|
state.bar = 'bar2'
|
||||||
|
await nextTick()
|
||||||
|
expect(spy).toHaveBeenCalledTimes(1)
|
||||||
|
|
||||||
|
state.foo.a.value++
|
||||||
|
state.foo.b++
|
||||||
|
await nextTick()
|
||||||
|
expect(spy).toHaveBeenCalledTimes(1)
|
||||||
|
|
||||||
|
state.bar = 'bar3'
|
||||||
|
await nextTick()
|
||||||
|
expect(spy).toHaveBeenCalledTimes(2)
|
||||||
|
})
|
||||||
|
it('watching reactive with deep: false', async () => {
|
||||||
|
const state = reactive({
|
||||||
|
foo: {
|
||||||
|
a: 2,
|
||||||
|
},
|
||||||
|
bar: 'bar',
|
||||||
|
})
|
||||||
|
|
||||||
|
const spy = vi.fn()
|
||||||
|
|
||||||
|
watch(state, spy, { deep: false })
|
||||||
|
|
||||||
|
state.foo.a++
|
||||||
|
await nextTick()
|
||||||
|
expect(spy).toHaveBeenCalledTimes(0)
|
||||||
|
|
||||||
|
state.bar = 'bar2'
|
||||||
|
await nextTick()
|
||||||
|
expect(spy).toHaveBeenCalledTimes(1)
|
||||||
|
})
|
||||||
|
|
||||||
test("effect should be removed from scope's effects after it is stopped", () => {
|
test("effect should be removed from scope's effects after it is stopped", () => {
|
||||||
const num = ref(0)
|
const num = ref(0)
|
||||||
let unwatch: () => void
|
let unwatch: () => void
|
||||||
|
|
|
@ -73,7 +73,7 @@ export interface WatchOptionsBase extends DebuggerOptions {
|
||||||
|
|
||||||
export interface WatchOptions<Immediate = boolean> extends WatchOptionsBase {
|
export interface WatchOptions<Immediate = boolean> extends WatchOptionsBase {
|
||||||
immediate?: Immediate
|
immediate?: Immediate
|
||||||
deep?: boolean
|
deep?: boolean | number
|
||||||
once?: boolean
|
once?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,14 +189,6 @@ function doWatch(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO remove in 3.5
|
|
||||||
if (__DEV__ && deep !== void 0 && typeof deep === 'number') {
|
|
||||||
warn(
|
|
||||||
`watch() "deep" option with number value will be used as watch depth in future versions. ` +
|
|
||||||
`Please use a boolean instead to avoid potential breakage.`,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (__DEV__ && !cb) {
|
if (__DEV__ && !cb) {
|
||||||
if (immediate !== undefined) {
|
if (immediate !== undefined) {
|
||||||
warn(
|
warn(
|
||||||
|
@ -228,11 +220,15 @@ function doWatch(
|
||||||
}
|
}
|
||||||
|
|
||||||
const instance = currentInstance
|
const instance = currentInstance
|
||||||
const reactiveGetter = (source: object) =>
|
const reactiveGetter = (source: object) => {
|
||||||
deep === true
|
// traverse will happen in wrapped getter below
|
||||||
? source // traverse will happen in wrapped getter below
|
if (deep) return source
|
||||||
: // for deep: false, only traverse root-level properties
|
// for `deep: false | 0` or shallow reactive, only traverse root-level properties
|
||||||
traverse(source, deep === false ? 1 : undefined)
|
if (isShallow(source) || deep === false || deep === 0)
|
||||||
|
return traverse(source, 1)
|
||||||
|
// for `deep: undefined` on a reactive object, deeply traverse all properties
|
||||||
|
return traverse(source)
|
||||||
|
}
|
||||||
|
|
||||||
let getter: () => any
|
let getter: () => any
|
||||||
let forceTrigger = false
|
let forceTrigger = false
|
||||||
|
@ -300,7 +296,8 @@ function doWatch(
|
||||||
|
|
||||||
if (cb && deep) {
|
if (cb && deep) {
|
||||||
const baseGetter = getter
|
const baseGetter = getter
|
||||||
getter = () => traverse(baseGetter())
|
const depth = deep === true ? Infinity : deep
|
||||||
|
getter = () => traverse(baseGetter(), depth)
|
||||||
}
|
}
|
||||||
|
|
||||||
let cleanup: (() => void) | undefined
|
let cleanup: (() => void) | undefined
|
||||||
|
|
Loading…
Reference in New Issue