Explorar el Código

fix(reactivity): ensure watcher with once: true are properly removed from effect scope (#11665)

yangxiuxiu hace 1 año
padre
commit
fbc0c42bcf

+ 10 - 10
packages/reactivity/src/watch.ts

@@ -206,18 +206,26 @@ export function watch(
     getter = () => traverse(baseGetter(), depth)
   }
 
+  const scope = getCurrentScope()
+  const watchHandle: WatchHandle = () => {
+    effect.stop()
+    if (scope) {
+      remove(scope.effects, effect)
+    }
+  }
+
   if (once) {
     if (cb) {
       const _cb = cb
       cb = (...args) => {
         _cb(...args)
-        effect.stop()
+        watchHandle()
       }
     } else {
       const _getter = getter
       getter = () => {
         _getter()
-        effect.stop()
+        watchHandle()
       }
     }
   }
@@ -317,14 +325,6 @@ export function watch(
     effect.run()
   }
 
-  const scope = getCurrentScope()
-  const watchHandle: WatchHandle = () => {
-    effect.stop()
-    if (scope) {
-      remove(scope.effects, effect)
-    }
-  }
-
   watchHandle.pause = effect.pause.bind(effect)
   watchHandle.resume = effect.resume.bind(effect)
   watchHandle.stop = watchHandle

+ 5 - 0
packages/runtime-core/__tests__/apiWatch.spec.ts

@@ -1771,6 +1771,11 @@ describe('api: watch', () => {
     expect(scope.effects.length).toBe(1)
     unwatch!()
     expect(scope.effects.length).toBe(0)
+
+    scope.run(() => {
+      watch(num, () => {}, { once: true, immediate: true })
+    })
+    expect(scope.effects.length).toBe(0)
   })
 
   // simplified case of VueUse syncRef