feat(runtime-core): add `once` option to watch (#9034)

This commit is contained in:
丶远方 2023-08-30 15:25:51 +08:00 committed by 三咲智子 Kevin Deng
parent edf2572615
commit a645e7aa51
No known key found for this signature in database
GPG Key ID: 69992F2250DFD93E
2 changed files with 61 additions and 8 deletions

View File

@ -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)
})
})

View File

@ -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
}