From d65675d902a2735a3471a6aba52134bd384b003b Mon Sep 17 00:00:00 2001 From: edison Date: Fri, 20 Jun 2025 08:38:24 +0800 Subject: [PATCH] fix(runtime-vapor): dynamic component work with insertionState (#13142) --- .../apiCreateDynamicComponent.spec.ts | 31 +++++++++++++++++++ .../src/apiCreateDynamicComponent.ts | 22 ++++++++++++- 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/packages/runtime-vapor/__tests__/apiCreateDynamicComponent.spec.ts b/packages/runtime-vapor/__tests__/apiCreateDynamicComponent.spec.ts index 87dc922dd..e912af285 100644 --- a/packages/runtime-vapor/__tests__/apiCreateDynamicComponent.spec.ts +++ b/packages/runtime-vapor/__tests__/apiCreateDynamicComponent.spec.ts @@ -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('
') }) + + test('switch dynamic component children', async () => { + const CompA = defineVaporComponent({ + setup() { + return template('
A
')() + }, + }) + const CompB = defineVaporComponent({ + setup() { + return template('
B
')() + }, + }) + + const current = shallowRef(CompA) + const { html } = define({ + setup() { + const t1 = template('
') + const n2 = t1() as any + setInsertionState(n2) + createDynamicComponent(() => current.value) + return n2 + }, + }).render() + + expect(html()).toBe('
A
') + + current.value = CompB + await nextTick() + expect(html()).toBe('
B
') + }) }) diff --git a/packages/runtime-vapor/src/apiCreateDynamicComponent.ts b/packages/runtime-vapor/src/apiCreateDynamicComponent.ts index 2126611d7..945e0f38d 100644 --- a/packages/runtime-vapor/src/apiCreateDynamicComponent.ts +++ b/packages/runtime-vapor/src/apiCreateDynamicComponent.ts @@ -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 }