fix(runtime-vapor): dynamic component work with insertionState (#13142)

This commit is contained in:
edison 2025-06-20 08:38:24 +08:00 committed by GitHub
parent b96447dd41
commit d65675d902
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 52 additions and 1 deletions

View 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>')
})
})

View 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
}