]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(runtime-vapor): dynamic component work with insertionState (#13142)
authoredison <daiwei521@126.com>
Fri, 20 Jun 2025 00:38:24 +0000 (08:38 +0800)
committerGitHub <noreply@github.com>
Fri, 20 Jun 2025 00:38:24 +0000 (08:38 +0800)
packages/runtime-vapor/__tests__/apiCreateDynamicComponent.spec.ts
packages/runtime-vapor/src/apiCreateDynamicComponent.ts

index 87dc922ddf666b437ec9c1d247d4c0db57256a90..e912af2851ab4719e0a198a51289a2f950f67626 100644 (file)
@@ -3,6 +3,7 @@ import { nextTick, resolveDynamicComponent } from '@vue/runtime-dom'
 import {
   createComponentWithFallback,
   createDynamicComponent,
+  defineVaporComponent,
   renderEffect,
   setHtml,
   setInsertionState,
@@ -79,4 +80,34 @@ describe('api: createDynamicComponent', () => {
     mount()
     expect(html()).toBe('<div><button>hi</button></div>')
   })
+
+  test('switch dynamic component children', async () => {
+    const CompA = defineVaporComponent({
+      setup() {
+        return template('<div>A</div>')()
+      },
+    })
+    const CompB = defineVaporComponent({
+      setup() {
+        return template('<div>B</div>')()
+      },
+    })
+
+    const current = shallowRef(CompA)
+    const { html } = define({
+      setup() {
+        const t1 = template('<div></div>')
+        const n2 = t1() as any
+        setInsertionState(n2)
+        createDynamicComponent(() => current.value)
+        return n2
+      },
+    }).render()
+
+    expect(html()).toBe('<div><div>A</div><!--dynamic-component--></div>')
+
+    current.value = CompB
+    await nextTick()
+    expect(html()).toBe('<div><div>B</div><!--dynamic-component--></div>')
+  })
 })
index 2126611d7182e1cb548d4179e09dd536c8f3c920..945e0f38d8729b9f97fb2ef42296d8dcf62b453f 100644 (file)
@@ -1,9 +1,15 @@
 import { resolveDynamicComponent } from '@vue/runtime-dom'
-import { DynamicFragment, type VaporFragment } from './block'
+import { DynamicFragment, type VaporFragment, insert } from './block'
 import { createComponentWithFallback } from './component'
 import { renderEffect } from './renderEffect'
 import type { RawProps } from './componentProps'
 import type { RawSlots } from './componentSlots'
+import {
+  insertionAnchor,
+  insertionParent,
+  resetInsertionState,
+} from './insertionState'
+import { isHydrating, locateHydrationNode } from './dom/hydration'
 
 export function createDynamicComponent(
   getter: () => any,
@@ -11,9 +17,18 @@ export function createDynamicComponent(
   rawSlots?: RawSlots | null,
   isSingleRoot?: boolean,
 ): VaporFragment {
+  const _insertionParent = insertionParent
+  const _insertionAnchor = insertionAnchor
+  if (isHydrating) {
+    locateHydrationNode()
+  } else {
+    resetInsertionState()
+  }
+
   const frag = __DEV__
     ? new DynamicFragment('dynamic-component')
     : new DynamicFragment()
+
   renderEffect(() => {
     const value = getter()
     frag.update(
@@ -27,5 +42,10 @@ export function createDynamicComponent(
       value,
     )
   })
+
+  if (!isHydrating && _insertionParent) {
+    insert(frag, _insertionParent, _insertionAnchor)
+  }
+
   return frag
 }