]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
feat(runtime-core): add `once` option to watch (#9034)
author丶远方 <yangpanteng@gmail.com>
Wed, 30 Aug 2023 07:25:51 +0000 (15:25 +0800)
committer三咲智子 Kevin Deng <sxzz@sxzz.moe>
Fri, 27 Oct 2023 14:28:40 +0000 (23:28 +0900)
packages/runtime-core/__tests__/apiWatch.spec.ts
packages/runtime-core/src/apiWatch.ts

index f24ce80b9dfba3546455b16917f5eeb35c715ba4..bddfc5ff541cdf298ece965d274ba96daabf00f2 100644 (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)
+  })
 })
index 1b85ba12d19b66586a38e10cb144f18f57758799..c307c4198a3028c925d72672d957725fdece64b8 100644 (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
 }