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

fix(suspense): defer clearing fallback vnode el in case it has dirs (#14080)

close #14078
linzhe 5 месяцев назад
Родитель
Сommit
c0f63ddbfa

+ 36 - 0
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', () => {
         `<div>444</div><div>555</div><div>666</div>`,
       )
     })
+
+    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)
+    })
   })
 })

+ 2 - 2
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) {