diff --git a/packages/runtime-core/__tests__/components/Suspense.spec.ts b/packages/runtime-core/__tests__/components/Suspense.spec.ts index c6683d6a257..4e8da3288f1 100644 --- a/packages/runtime-core/__tests__/components/Suspense.spec.ts +++ b/packages/runtime-core/__tests__/components/Suspense.spec.ts @@ -24,6 +24,7 @@ import { shallowRef, watch, watchEffect, + withDirectives, } from '@vue/runtime-test' import { computed, createApp, defineComponent, inject, provide } from 'vue' import type { RawSlots } from 'packages/runtime-core/src/componentSlots' @@ -2358,5 +2359,40 @@ describe('Suspense', () => { `
444
555
666
`, ) }) + + test('should call unmounted directive once when fallback is replaced by resolved async component', async () => { + const Comp = { + render() { + return h('div', null, 'comp') + }, + } + const Foo = defineAsyncComponent({ + render() { + return h(Comp) + }, + }) + const unmounted = vi.fn(el => { + el.foo = null + }) + const vDir = { + unmounted, + } + const App = { + setup() { + return () => { + return h(Suspense, null, { + fallback: () => withDirectives(h('div'), [[vDir, true]]), + default: () => h(Foo), + }) + } + }, + } + const root = nodeOps.createElement('div') + render(h(App), root) + + await Promise.all(deps) + await nextTick() + expect(unmounted).toHaveBeenCalledTimes(1) + }) }) }) diff --git a/packages/runtime-core/src/components/Suspense.ts b/packages/runtime-core/src/components/Suspense.ts index d14e96be3d0..0c8b6c28e8d 100644 --- a/packages/runtime-core/src/components/Suspense.ts +++ b/packages/runtime-core/src/components/Suspense.ts @@ -20,6 +20,7 @@ import { type RendererInternals, type RendererNode, type SetupRenderEffectFn, + queuePostRenderEffect, } from '../renderer' import { queuePostFlushCb } from '../scheduler' import { filterSingleRoot, updateHOCHostEl } from '../componentRenderUtils' @@ -576,9 +577,8 @@ function createSuspenseBoundary( } unmount(activeBranch, parentComponent, suspense, true) // clear el reference from fallback vnode to allow GC - // only clear immediately if there's no delayed transition if (!delayEnter && isInFallback && vnode.ssFallback) { - vnode.ssFallback.el = null + queuePostRenderEffect(() => (vnode.ssFallback!.el = null), suspense) } } if (!delayEnter) {