From: daiwei Date: Mon, 21 Jul 2025 00:29:31 +0000 (+0800) Subject: fix(runtime-vapor): render slot fallback if slot content is not a valid block X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=2ba4dc0d0720b0cc8639e04680dd157c71913299;p=thirdparty%2Fvuejs%2Fcore.git fix(runtime-vapor): render slot fallback if slot content is not a valid block close #13668 --- diff --git a/packages/runtime-vapor/__tests__/componentSlots.spec.ts b/packages/runtime-vapor/__tests__/componentSlots.spec.ts index 58076fff9e..f54137724c 100644 --- a/packages/runtime-vapor/__tests__/componentSlots.spec.ts +++ b/packages/runtime-vapor/__tests__/componentSlots.spec.ts @@ -502,5 +502,64 @@ describe('component: slots', () => { await nextTick() expect(host.innerHTML).toBe('

') }) + + test('render fallback when slot content is not valid', async () => { + const Child = { + setup() { + return createSlot('default', null, () => + document.createTextNode('fallback'), + ) + }, + } + + const { html } = define({ + setup() { + return createComponent(Child, null, { + default: () => { + return template('')() + }, + }) + }, + }).render() + + expect(html()).toBe('fallback') + }) + + test('render fallback when v-if condition is false', async () => { + const Child = { + setup() { + return createSlot('default', null, () => + document.createTextNode('fallback'), + ) + }, + } + + const toggle = ref(false) + + const { html } = define({ + setup() { + return createComponent(Child, null, { + default: () => { + return createIf( + () => toggle.value, + () => { + return document.createTextNode('content') + }, + ) + }, + }) + }, + }).render() + + expect(html()).toBe('fallback') + + toggle.value = true + await nextTick() + expect(html()).toBe('content') + + toggle.value = false + await nextTick() + expect(html()).toBe('fallback') + }) }) }) diff --git a/packages/runtime-vapor/src/block.ts b/packages/runtime-vapor/src/block.ts index e021ce84b0..50a453dc48 100644 --- a/packages/runtime-vapor/src/block.ts +++ b/packages/runtime-vapor/src/block.ts @@ -67,9 +67,15 @@ export class DynamicFragment extends VaporFragment { if (this.fallback && !isValidBlock(this.nodes)) { parent && remove(this.nodes, parent) - this.nodes = - (this.scope || (this.scope = new EffectScope())).run(this.fallback) || - [] + // if current nodes is a DynamicFragment, call its update with the fallback + // to handle nested dynamic fragment + if (this.nodes instanceof DynamicFragment) { + this.nodes.update(this.fallback) + } else { + this.nodes = + (this.scope || (this.scope = new EffectScope())).run(this.fallback) || + [] + } parent && insert(this.nodes, parent, this.anchor) } diff --git a/packages/runtime-vapor/src/componentSlots.ts b/packages/runtime-vapor/src/componentSlots.ts index 100c99cdb8..4215548f2e 100644 --- a/packages/runtime-vapor/src/componentSlots.ts +++ b/packages/runtime-vapor/src/componentSlots.ts @@ -126,6 +126,7 @@ export function createSlot( const renderSlot = () => { const slot = getSlot(rawSlots, isFunction(name) ? name() : name) if (slot) { + fragment.fallback = fallback // create and cache bound version of the slot to make it stable // so that we avoid unnecessary updates if it resolves to the same slot fragment.update(