Просмотр исходного кода

fix(runtime-core): ensure tracking is paused when emit() calls handler so it can safely be called in effects

fix: #6669
Thorsten Luenborg 3 лет назад
Родитель
Сommit
1c02e5a9c2

+ 35 - 1
packages/runtime-core/__tests__/componentEmits.spec.ts

@@ -7,7 +7,9 @@ import {
   h,
   nodeOps,
   toHandlers,
-  nextTick
+  nextTick,
+  ref,
+  watchEffect
 } from '@vue/runtime-test'
 import { isEmitListener } from '../src/componentEmits'
 
@@ -431,4 +433,36 @@ describe('component: emit', () => {
     await nextTick()
     expect(fn).not.toHaveBeenCalled()
   })
+
+  test('should not track during listener execution', async () => {
+    const counter = ref(0)
+    const Comp = defineComponent({
+      emits: ['interaction'],
+      setup(props, { emit }) {
+        const doEmit = ref(true)
+        watchEffect(() => {
+          if (doEmit.value) emit('interaction')
+        })
+        return () => h('div')
+      }
+    })
+    const el = nodeOps.createElement('div')
+    render(
+      h(Comp, {
+        onInteraction: async () => {
+          if (counter.value < 5) {
+            await nextTick()
+            counter.value++
+          }
+        }
+      }),
+      el
+    )
+
+    await nextTick()
+    await nextTick()
+    await nextTick()
+
+    expect(counter.value).toBe(1)
+  })
 })

+ 3 - 0
packages/runtime-core/src/componentEmits.ts

@@ -27,6 +27,7 @@ import {
   compatModelEventPrefix,
   compatModelEmit
 } from './compat/componentVModel'
+import { pauseTracking, resetTracking } from '@vue/reactivity'
 
 export type ObjectEmitsOptions = Record<
   string,
@@ -161,12 +162,14 @@ export function emit(
   }
 
   if (handler) {
+    pauseTracking()
     callWithAsyncErrorHandling(
       handler,
       instance,
       ErrorCodes.COMPONENT_EVENT_HANDLER,
       args
     )
+    resetTracking()
   }
 
   const onceHandler = props[handlerName + `Once`]