feat(runtime-core): add `once` option to watch (#9034)
This commit is contained in:
parent
edf2572615
commit
a645e7aa51
|
@ -1205,4 +1205,42 @@ describe('api: watch', () => {
|
|||
expect(countWE).toBe(3)
|
||||
expect(countW).toBe(2)
|
||||
})
|
||||
|
||||
const options = [
|
||||
{ name: 'only trigger once watch' },
|
||||
{
|
||||
deep: true,
|
||||
name: 'only trigger once watch with deep'
|
||||
},
|
||||
{
|
||||
flush: 'sync',
|
||||
name: 'only trigger once watch with flush: sync'
|
||||
},
|
||||
{
|
||||
flush: 'pre',
|
||||
name: 'only trigger once watch with flush: pre'
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
name: 'only trigger once watch with immediate'
|
||||
}
|
||||
] as const
|
||||
test.each(options)('$name', async option => {
|
||||
const count = ref(0)
|
||||
const cb = vi.fn()
|
||||
|
||||
watch(count, cb, { once: true, ...option })
|
||||
|
||||
count.value++
|
||||
await nextTick()
|
||||
|
||||
expect(count.value).toBe(1)
|
||||
expect(cb).toHaveBeenCalledTimes(1)
|
||||
|
||||
count.value++
|
||||
await nextTick()
|
||||
|
||||
expect(count.value).toBe(2)
|
||||
expect(cb).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
})
|
||||
|
|
|
@ -75,6 +75,7 @@ export interface WatchOptionsBase extends DebuggerOptions {
|
|||
export interface WatchOptions<Immediate = boolean> extends WatchOptionsBase {
|
||||
immediate?: Immediate
|
||||
deep?: boolean
|
||||
once?: boolean
|
||||
}
|
||||
|
||||
export type WatchStopHandle = () => void
|
||||
|
@ -172,8 +173,16 @@ export function watch<T = any, Immediate extends Readonly<boolean> = false>(
|
|||
function doWatch(
|
||||
source: WatchSource | WatchSource[] | WatchEffect | object,
|
||||
cb: WatchCallback | null,
|
||||
{ immediate, deep, flush, onTrack, onTrigger }: WatchOptions = EMPTY_OBJ
|
||||
{ immediate, deep, flush, once, onTrack, onTrigger }: WatchOptions = EMPTY_OBJ
|
||||
): WatchStopHandle {
|
||||
if (cb && once) {
|
||||
const _cb = cb
|
||||
cb = (...args) => {
|
||||
_cb(...args)
|
||||
unwatch()
|
||||
}
|
||||
}
|
||||
|
||||
if (__DEV__ && !cb) {
|
||||
if (immediate !== undefined) {
|
||||
warn(
|
||||
|
@ -187,6 +196,12 @@ function doWatch(
|
|||
`watch(source, callback, options?) signature.`
|
||||
)
|
||||
}
|
||||
if (once !== undefined) {
|
||||
warn(
|
||||
`watch() "once" option is only respected when using the ` +
|
||||
`watch(source, callback, options?) signature.`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const warnInvalidSource = (s: unknown) => {
|
||||
|
@ -363,6 +378,13 @@ function doWatch(
|
|||
|
||||
const effect = new ReactiveEffect(getter, scheduler)
|
||||
|
||||
const unwatch = () => {
|
||||
effect.stop()
|
||||
if (instance && instance.scope) {
|
||||
remove(instance.scope.effects!, effect)
|
||||
}
|
||||
}
|
||||
|
||||
if (__DEV__) {
|
||||
effect.onTrack = onTrack
|
||||
effect.onTrigger = onTrigger
|
||||
|
@ -384,13 +406,6 @@ function doWatch(
|
|||
effect.run()
|
||||
}
|
||||
|
||||
const unwatch = () => {
|
||||
effect.stop()
|
||||
if (instance && instance.scope) {
|
||||
remove(instance.scope.effects!, effect)
|
||||
}
|
||||
}
|
||||
|
||||
if (__SSR__ && ssrCleanup) ssrCleanup.push(unwatch)
|
||||
return unwatch
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue