diff --git a/packages/runtime-vapor/__tests__/apiCreateDynamicComponent.spec.ts b/packages/runtime-vapor/__tests__/apiCreateDynamicComponent.spec.ts index 89514e17701..1ee50e78591 100644 --- a/packages/runtime-vapor/__tests__/apiCreateDynamicComponent.spec.ts +++ b/packages/runtime-vapor/__tests__/apiCreateDynamicComponent.spec.ts @@ -148,4 +148,35 @@ describe('api: createDynamicComponent', () => { await nextTick() expect(html()).toBe('
B
') }) + + test('fallback with dynamic slots', async () => { + const slotName = ref('default') + const { html } = define({ + setup() { + return createDynamicComponent(() => 'div', null, { + $: [ + () => ({ + name: slotName.value, + fn: () => template('hi')(), + }), + ] as any, + }) + }, + }).render() + + expect(html()).toBe( + '
hi
', + ) + + // update slot name + slotName.value = 'custom' + await nextTick() + expect(html()).toBe('
') + + slotName.value = 'default' + await nextTick() + expect(html()).toBe( + '
hi
', + ) + }) }) diff --git a/packages/runtime-vapor/__tests__/hydration.spec.ts b/packages/runtime-vapor/__tests__/hydration.spec.ts index 17174408eee..7094a0f60af 100644 --- a/packages/runtime-vapor/__tests__/hydration.spec.ts +++ b/packages/runtime-vapor/__tests__/hydration.spec.ts @@ -1065,6 +1065,34 @@ describe('Vapor Mode hydration', () => { `, ) }) + + test('dynamic component fallback with dynamic slots', async () => { + const data = ref({ + name: 'default', + msg: 'foo', + }) + const { container } = await testHydration( + ``, + {}, + data, + ) + + expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot( + `"
foo
"`, + ) + + data.value.msg = 'bar' + await nextTick() + expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot( + `"
bar
"`, + ) + }) }) describe('if', () => { diff --git a/packages/runtime-vapor/src/component.ts b/packages/runtime-vapor/src/component.ts index e850f08932d..d083d7404cb 100644 --- a/packages/runtime-vapor/src/component.ts +++ b/packages/runtime-vapor/src/component.ts @@ -665,9 +665,16 @@ export function createComponentWithFallback( setCurrentHydrationNode(el.firstChild) } if (rawSlots.$) { - // TODO dynamic slot fragment + // ssr output does not contain the slot anchor, use an empty string + // as the anchor label to avoid slot anchor search errors + const frag = new DynamicFragment( + isHydrating ? '' : __DEV__ ? 'slot' : undefined, + ) + renderEffect(() => frag.update(getSlot(rawSlots as RawSlots, 'default'))) + if (!isHydrating) insert(frag, el) } else { - insert(getSlot(rawSlots as RawSlots, 'default')!(), el) + const block = getSlot(rawSlots as RawSlots, 'default')!() + if (!isHydrating) insert(block, el) } if (isHydrating) { setCurrentHydrationNode(nextNode)