Explorar o código

fix(runtime-vapor): invalidate pending mount hooks on deactivate/unmount

daiwei hai 1 mes
pai
achega
0d8c519e83

+ 5 - 1
packages/runtime-core/src/index.ts

@@ -635,7 +635,11 @@ export { devtoolsComponentAdded } from './devtools'
 /**
  * @internal
  */
-export { performTransitionEnter, performTransitionLeave } from './renderer'
+export {
+  performTransitionEnter,
+  performTransitionLeave,
+  invalidateMount,
+} from './renderer'
 /**
  * @internal
  */

+ 32 - 0
packages/runtime-vapor/__tests__/component.spec.ts

@@ -4,6 +4,7 @@ import {
   type Ref,
   inject,
   nextTick,
+  onBeforeMount,
   onMounted,
   onUpdated,
   provide,
@@ -559,6 +560,37 @@ describe('component', () => {
       'Unhandled error during execution of setup function',
     ).not.toHaveBeenWarned()
   })
+
+  it('should invalidate pending mounted hooks when unmounted before flush', async () => {
+    const mountedSpy = vi.fn()
+    const show = ref(false)
+
+    const Child = defineVaporComponent({
+      setup() {
+        onBeforeMount(() => {
+          show.value = false
+        })
+        onMounted(mountedSpy)
+        return template('<div>child</div>')()
+      },
+    })
+
+    define({
+      setup() {
+        return createIf(
+          () => show.value,
+          () => createComponent(Child),
+        )
+      },
+    }).render()
+
+    expect(mountedSpy).toHaveBeenCalledTimes(0)
+
+    show.value = true
+    await nextTick()
+
+    expect(mountedSpy).toHaveBeenCalledTimes(0)
+  })
 })
 
 function getEffectsCount(scope: EffectScope): number {

+ 40 - 0
packages/runtime-vapor/__tests__/components/KeepAlive.spec.ts

@@ -1820,4 +1820,44 @@ describe('VaporKeepAlive', () => {
       expect(inputEl.value).toBe('vdom')
     })
   })
+
+  test('should invalidate pending mount/activated hooks when deactivated before post flush', async () => {
+    const mountedSpy = vi.fn()
+    const activatedSpy = vi.fn()
+
+    const show = ref(false)
+
+    const Child = defineVaporComponent({
+      name: 'Child',
+      setup() {
+        onBeforeMount(() => {
+          show.value = false
+        })
+        onMounted(mountedSpy)
+        onActivated(activatedSpy)
+        return template('<div>child</div>')()
+      },
+    })
+
+    define({
+      setup() {
+        return createComponent(VaporKeepAlive, null, {
+          default: () =>
+            createIf(
+              () => show.value,
+              () => createComponent(Child),
+            ),
+        })
+      },
+    }).render()
+
+    expect(mountedSpy).toHaveBeenCalledTimes(0)
+    expect(activatedSpy).toHaveBeenCalledTimes(0)
+
+    show.value = true
+    await nextTick()
+
+    expect(mountedSpy).toHaveBeenCalledTimes(0)
+    expect(activatedSpy).toHaveBeenCalledTimes(0)
+  })
 })

+ 3 - 0
packages/runtime-vapor/src/component.ts

@@ -23,6 +23,7 @@ import {
   expose,
   getComponentName,
   getFunctionalFallthrough,
+  invalidateMount,
   isAsyncWrapper,
   isKeepAlive,
   markAsyncBoundary,
@@ -935,6 +936,8 @@ export function unmountComponent(
     if (__DEV__) {
       unregisterHMR(instance)
     }
+    invalidateMount(instance.m)
+    invalidateMount(instance.a)
     if (instance.bum) {
       invokeArrayFns(instance.bum)
     }

+ 4 - 0
packages/runtime-vapor/src/components/KeepAlive.ts

@@ -8,6 +8,7 @@ import {
   currentInstance,
   devtoolsComponentAdded,
   getComponentName,
+  invalidateMount,
   isAsyncWrapper,
   isVNode,
   matches,
@@ -478,6 +479,9 @@ export function deactivate(
   instance: VaporComponentInstance,
   container: ParentNode,
 ): void {
+  invalidateMount(instance.m)
+  invalidateMount(instance.a)
+
   move(instance.block, container, null, MoveType.LEAVE, instance)
 
   queuePostFlushCb(() => {