From: daiwei Date: Fri, 22 Aug 2025 07:39:15 +0000 (+0800) Subject: fix(runtime-vapor): improve fallback handling support chaining X-Git-Url: http://git.ipfire.org/gitweb/gitweb.cgi?a=commitdiff_plain;h=refs%2Fpull%2F13408%2Fhead;p=thirdparty%2Fvuejs%2Fcore.git fix(runtime-vapor): improve fallback handling support chaining --- diff --git a/packages/runtime-vapor/__tests__/componentSlots.spec.ts b/packages/runtime-vapor/__tests__/componentSlots.spec.ts index ea9c07dc02..16022d5150 100644 --- a/packages/runtime-vapor/__tests__/componentSlots.spec.ts +++ b/packages/runtime-vapor/__tests__/componentSlots.spec.ts @@ -870,971 +870,137 @@ describe('component: slots', () => { expect(host.innerHTML).toBe('barbar') }) - describe('vdom interop', () => { - const createVaporSlot = (fallbackText = 'fallback') => { - return defineVaporComponent({ - setup() { - const n0 = createSlot('foo', null, () => { - const n2 = template(`
${fallbackText}
`)() - return n2 - }) - return n0 - }, - }) - } - - const createVdomSlot = (fallbackText = 'fallback') => { - return { - render(this: any) { - return renderSlot(this.$slots, 'foo', {}, () => [ - h('div', fallbackText), - ]) - }, - } - } - - const createVaporForwardedSlot = ( - targetComponent: any, - fallbackText?: string, - ) => { - return defineVaporComponent({ - setup() { - const createForwardedSlot = forwardedSlotCreator() - const n2 = createComponent( - targetComponent, - null, - { - foo: () => { - return fallbackText - ? createForwardedSlot('foo', null, () => { - const n2 = template(`
${fallbackText}
`)() - return n2 - }) - : createForwardedSlot('foo', null) - }, - }, - true, - ) - return n2 - }, - }) - } - - const createVdomForwardedSlot = ( - targetComponent: any, - fallbackText?: string, - ) => { - return { - render(this: any) { - return h(targetComponent, null, { - foo: () => [ - fallbackText - ? renderSlot(this.$slots, 'foo', {}, () => [ - h('div', fallbackText), - ]) - : renderSlot(this.$slots, 'foo'), - ], - _: 3 /* FORWARDED */, - }) - }, - } - } - - const createMultipleVaporForwardedSlots = ( - targetComponent: any, - count: number, - ) => { - let current = targetComponent - for (let i = 0; i < count; i++) { - current = createVaporForwardedSlot(current) - } - return current - } - - const createMultipleVdomForwardedSlots = ( - targetComponent: any, - count: number, - ) => { - let current = targetComponent - for (let i = 0; i < count; i++) { - current = createVdomForwardedSlot(current) - } - return current - } - - const createTestApp = ( - rootComponent: any, - foo: Ref, - show: Ref, - ) => { - return { - setup() { - return () => - h( - rootComponent, - null, - createSlots({ _: 2 /* DYNAMIC */ } as any, [ - show.value - ? { - name: 'foo', - fn: () => [h('span', foo.value)], - key: '0', - } - : undefined, - ]), - ) - }, - } - } - - const createEmptyTestApp = (rootComponent: any) => { - return { - setup() { - return () => h(rootComponent) - }, - } - } - - test('vdom slot > vapor forwarded slot > vapor slot', async () => { - const foo = ref('foo') - const show = ref(true) - - const VaporSlot = createVaporSlot() - const VaporForwardedSlot = createVaporForwardedSlot(VaporSlot) - const App = createTestApp(VaporForwardedSlot, foo, show) - - const root = document.createElement('div') - createApp(App).use(vaporInteropPlugin).mount(root) - expect(root.innerHTML).toBe('foo') - - foo.value = 'bar' - await nextTick() - expect(root.innerHTML).toBe('bar') - - show.value = false - await nextTick() - expect(root.innerHTML).toBe('
fallback
') - }) - - test('vdom slot > vapor forwarded slot(with fallback) > vapor slot', async () => { - const foo = ref('foo') - const show = ref(true) - - const VaporSlot = createVaporSlot() - const VaporForwardedSlotWithFallback = createVaporForwardedSlot( - VaporSlot, - 'forwarded fallback', - ) - const App = createTestApp(VaporForwardedSlotWithFallback, foo, show) - - const root = document.createElement('div') - createApp(App).use(vaporInteropPlugin).mount(root) - expect(root.innerHTML).toBe('foo') - - foo.value = 'bar' - await nextTick() - expect(root.innerHTML).toBe('bar') - - show.value = false - await nextTick() - expect(root.innerHTML).toBe('
forwarded fallback
') - }) - - test('vdom slot > vapor forwarded slot > vdom slot', async () => { - const foo = ref('foo') - const show = ref(true) - - const VdomSlot = createVdomSlot() - const VaporForwardedSlot = createVaporForwardedSlot(VdomSlot) - const App = createTestApp(VaporForwardedSlot, foo, show) - - const root = document.createElement('div') - createApp(App).use(vaporInteropPlugin).mount(root) - expect(root.innerHTML).toBe('foo') - - foo.value = 'bar' - await nextTick() - expect(root.innerHTML).toBe('bar') - - show.value = false - await nextTick() - expect(root.innerHTML).toBe('
fallback
') - - show.value = true - await nextTick() - expect(root.innerHTML).toBe('bar') - }) - - test('vdom slot > vapor forwarded slot(with fallback) > vdom slot', async () => { - const foo = ref('foo') - const show = ref(true) - - const VdomSlot = createVdomSlot() - const VaporForwardedSlotWithFallback = createVaporForwardedSlot( - VdomSlot, - 'forwarded fallback', - ) - const App = createTestApp(VaporForwardedSlotWithFallback, foo, show) - - const root = document.createElement('div') - createApp(App).use(vaporInteropPlugin).mount(root) - expect(root.innerHTML).toBe('foo') - - foo.value = 'bar' - await nextTick() - expect(root.innerHTML).toBe('bar') - - show.value = false - await nextTick() - expect(root.innerHTML).toBe('
forwarded fallback
') - }) - - test('vdom slot > vapor forwarded slot > vdom forwarded slot > vapor slot', async () => { - const foo = ref('foo') - const show = ref(true) - - const VaporSlot = createVaporSlot() - const VdomForwardedSlot = createVdomForwardedSlot(VaporSlot) - const VaporForwardedSlot = createVaporForwardedSlot(VdomForwardedSlot) - const App = createTestApp(VaporForwardedSlot, foo, show) - - const root = document.createElement('div') - createApp(App).use(vaporInteropPlugin).mount(root) - expect(root.innerHTML).toBe('foo') - - foo.value = 'bar' - await nextTick() - expect(root.innerHTML).toBe('bar') - - show.value = false - await nextTick() - expect(root.innerHTML).toBe('
fallback
') - - show.value = true - await nextTick() - expect(root.innerHTML).toBe('bar') - }) - - test('vdom slot > vapor forwarded slot(with fallback) > vdom forwarded slot > vapor slot', async () => { - const foo = ref('foo') - const show = ref(true) - - const VaporSlot = createVaporSlot() - const VdomForwardedSlot = createVdomForwardedSlot(VaporSlot) - const VaporForwardedSlotWithFallback = createVaporForwardedSlot( - VdomForwardedSlot, - 'forwarded fallback', - ) - const App = createTestApp(VaporForwardedSlotWithFallback, foo, show) - - const root = document.createElement('div') - createApp(App).use(vaporInteropPlugin).mount(root) - expect(root.innerHTML).toBe('foo') - - foo.value = 'bar' - await nextTick() - expect(root.innerHTML).toBe('bar') - - show.value = false - await nextTick() - expect(root.innerHTML).toBe('
forwarded fallback
') - - show.value = true - await nextTick() - expect(root.innerHTML).toBe('bar') - }) - - test('vdom slot > vapor forwarded slot > vdom forwarded slot(with fallback) > vapor slot', async () => { - const foo = ref('foo') - const show = ref(true) - - const VaporSlot = createVaporSlot() - const VdomForwardedSlotWithFallback = createVdomForwardedSlot( - VaporSlot, - 'vdom fallback', - ) - const VaporForwardedSlot = createVaporForwardedSlot( - VdomForwardedSlotWithFallback, - ) - const App = createTestApp(VaporForwardedSlot, foo, show) - - const root = document.createElement('div') - createApp(App).use(vaporInteropPlugin).mount(root) - expect(root.innerHTML).toBe('foo') - - foo.value = 'bar' - await nextTick() - expect(root.innerHTML).toBe('bar') - - show.value = false - await nextTick() - expect(root.innerHTML).toBe('
vdom fallback
') - - show.value = true - await nextTick() - expect(root.innerHTML).toBe('bar') - }) - - test('vdom slot(empty) > vapor forwarded slot > vdom forwarded slot(with fallback) > vapor slot', async () => { - const VaporSlot = createVaporSlot() - const VdomForwardedSlotWithFallback = createVdomForwardedSlot( - VaporSlot, - 'vdom fallback', - ) - const VaporForwardedSlot = createVaporForwardedSlot( - VdomForwardedSlotWithFallback, - ) - const App = createEmptyTestApp(VaporForwardedSlot) - - const root = document.createElement('div') - createApp(App).use(vaporInteropPlugin).mount(root) - expect(root.innerHTML).toBe('
vdom fallback
') - }) - - test('vdom slot > vapor forwarded slot > vdom forwarded slot > vdom slot', async () => { - const foo = ref('foo') - const show = ref(true) - - const VdomSlot = createVdomSlot() - const VdomForwardedSlot = createVdomForwardedSlot(VdomSlot) - const VaporForwardedSlot = createVaporForwardedSlot(VdomForwardedSlot) - const App = createTestApp(VaporForwardedSlot, foo, show) - - const root = document.createElement('div') - createApp(App).use(vaporInteropPlugin).mount(root) - expect(root.innerHTML).toBe('foo') - - foo.value = 'bar' - await nextTick() - expect(root.innerHTML).toBe('bar') - - show.value = false - await nextTick() - expect(root.innerHTML).toBe('
fallback
') - - show.value = true - await nextTick() - expect(root.innerHTML).toBe('bar') - }) - - test('vdom slot > vapor forwarded slot(with fallback) > vdom forwarded slot > vdom slot', async () => { - const foo = ref('foo') - const show = ref(true) - - const VdomSlot = createVdomSlot() - const VdomForwardedSlot = createVdomForwardedSlot(VdomSlot) - const VaporForwardedSlotWithFallback = createVaporForwardedSlot( - VdomForwardedSlot, - 'vapor fallback', - ) - const App = createTestApp(VaporForwardedSlotWithFallback, foo, show) - - const root = document.createElement('div') - createApp(App).use(vaporInteropPlugin).mount(root) - expect(root.innerHTML).toBe('foo') - - foo.value = 'bar' - await nextTick() - expect(root.innerHTML).toBe('bar') - - show.value = false - await nextTick() - expect(root.innerHTML).toBe('
vapor fallback
') - - show.value = true - await nextTick() - expect(root.innerHTML).toBe('bar') - }) - - test('vdom slot > vapor forwarded slot > vdom forwarded slot(with fallback) > vdom slot', async () => { - const foo = ref('foo') - const show = ref(true) - - const VdomSlot = createVdomSlot() - - const VdomForwardedSlotWithFallback = createVdomForwardedSlot( - VdomSlot, - 'vdom fallback', - ) - const VaporForwardedSlot = createVaporForwardedSlot( - VdomForwardedSlotWithFallback, - ) - const App = createTestApp(VaporForwardedSlot, foo, show) - - const root = document.createElement('div') - createApp(App).use(vaporInteropPlugin).mount(root) - expect(root.innerHTML).toBe('foo') - - foo.value = 'bar' - await nextTick() - expect(root.innerHTML).toBe('bar') - - show.value = false - await nextTick() - expect(root.innerHTML).toBe('
vdom fallback
') - - show.value = true - await nextTick() - expect(root.innerHTML).toBe('bar') - }) - - test('vdom slot > vapor forwarded slot (multiple) > vdom forwarded slot > vdom slot', async () => { - const foo = ref('foo') - const show = ref(true) - - const VdomSlot = createVdomSlot() - const VdomForwardedSlot = createVdomForwardedSlot(VdomSlot) - const VaporForwardedSlot = createMultipleVaporForwardedSlots( - VdomForwardedSlot, - 3, - ) - const App = createTestApp(VaporForwardedSlot, foo, show) - - const root = document.createElement('div') - createApp(App).use(vaporInteropPlugin).mount(root) - expect(root.innerHTML).toBe('foo') - - foo.value = 'bar' - await nextTick() - expect(root.innerHTML).toBe('bar') - - show.value = false - await nextTick() - expect(root.innerHTML).toBe('
fallback
') - - show.value = true - await nextTick() - expect(root.innerHTML).toBe('bar') - }) - - test('vdom slot > vapor forwarded slot (multiple) > vdom forwarded slot(with fallback) > vdom slot', async () => { - const foo = ref('foo') - const show = ref(true) - - const VdomSlot = createVdomSlot() - const VdomForwardedSlotWithFallback = createVdomForwardedSlot( - VdomSlot, - 'vdom fallback', - ) - const VaporForwardedSlot = createMultipleVaporForwardedSlots( - VdomForwardedSlotWithFallback, - 3, - ) - const App = createTestApp(VaporForwardedSlot, foo, show) - - const root = document.createElement('div') - createApp(App).use(vaporInteropPlugin).mount(root) - expect(root.innerHTML).toBe('foo') - - foo.value = 'bar' - await nextTick() - expect(root.innerHTML).toBe('bar') - - show.value = false - await nextTick() - expect(root.innerHTML).toBe( - '
vdom fallback
', - ) - - show.value = true - await nextTick() - expect(root.innerHTML).toBe('bar') - }) - - test('vdom slot > vdom forwarded slot > vapor slot', async () => { - const foo = ref('foo') - const show = ref(true) - - const VaporSlot = createVaporSlot() - const VdomForwardedSlot = createVdomForwardedSlot(VaporSlot) - const App = createTestApp(VdomForwardedSlot, foo, show) - - const root = document.createElement('div') - createApp(App).use(vaporInteropPlugin).mount(root) - expect(root.innerHTML).toBe('foo') - - foo.value = 'bar' - await nextTick() - expect(root.innerHTML).toBe('bar') - - show.value = false - await nextTick() - expect(root.innerHTML).toBe('
fallback
') - - show.value = true - await nextTick() - expect(root.innerHTML).toBe('bar') - }) - - test('vdom slot > vdom forwarded slot > vapor forwarded slot > vapor slot', async () => { - const foo = ref('foo') - const show = ref(true) - - const VaporSlot = createVaporSlot() - const VaporForwardedSlot = createVaporForwardedSlot(VaporSlot) - const VdomForwardedSlot = createVdomForwardedSlot(VaporForwardedSlot) - const App = createTestApp(VdomForwardedSlot, foo, show) - - const root = document.createElement('div') - createApp(App).use(vaporInteropPlugin).mount(root) - expect(root.innerHTML).toBe('foo') - - foo.value = 'bar' - await nextTick() - expect(root.innerHTML).toBe('bar') - - show.value = false - await nextTick() - expect(root.innerHTML).toBe('
fallback
') - - show.value = true - await nextTick() - expect(root.innerHTML).toBe('bar') - }) - - test('vdom slot > vdom forwarded slot (multiple) > vapor forwarded slot > vdom slot', async () => { - const foo = ref('foo') - const show = ref(true) - - const VaporSlot = createVaporSlot() - const VaporForwardedSlot = createVaporForwardedSlot(VaporSlot) - const VdomForwardedSlot = createMultipleVdomForwardedSlots( - VaporForwardedSlot, - 3, - ) - const App = createTestApp(VdomForwardedSlot, foo, show) - - const root = document.createElement('div') - createApp(App).use(vaporInteropPlugin).mount(root) - expect(root.innerHTML).toBe('foo') - - foo.value = 'bar' - await nextTick() - expect(root.innerHTML).toBe('bar') - - show.value = false - await nextTick() - expect(root.innerHTML).toBe('
fallback
') - - show.value = true - await nextTick() - expect(root.innerHTML).toBe('bar') - }) - - test('vdom slot > vdom forwarded slot (multiple) > vapor forwarded slot(with fallback) > vdom slot', async () => { - const foo = ref('foo') - const show = ref(true) - - const VaporSlot = createVaporSlot() - const VaporForwardedSlot = createVaporForwardedSlot( - VaporSlot, - 'vapor fallback', - ) - const VdomForwardedSlot = createMultipleVdomForwardedSlots( - VaporForwardedSlot, - 3, - ) - const App = createTestApp(VdomForwardedSlot, foo, show) - - const root = document.createElement('div') - createApp(App).use(vaporInteropPlugin).mount(root) - expect(root.innerHTML).toBe('foo') - - foo.value = 'bar' - await nextTick() - expect(root.innerHTML).toBe('bar') - - show.value = false - await nextTick() - expect(root.innerHTML).toBe('
vapor fallback
') - - show.value = true - await nextTick() - expect(root.innerHTML).toBe('bar') - }) - - test('vdom slot > vapor forwarded slot > vapor forwarded slot > vdom slot', async () => { - const foo = ref('foo') - const show = ref(true) - - const VdomSlot = createVdomSlot() - const VaporForwardedSlot1 = createMultipleVaporForwardedSlots( - VdomSlot, - 2, - ) - const App = createTestApp(VaporForwardedSlot1, foo, show) - - const root = document.createElement('div') - createApp(App).use(vaporInteropPlugin).mount(root) - expect(root.innerHTML).toBe('foo') - - foo.value = 'bar' - await nextTick() - expect(root.innerHTML).toBe('bar') - - show.value = false - await nextTick() - expect(root.innerHTML).toBe('
fallback
') - - show.value = true - await nextTick() - expect(root.innerHTML).toBe('bar') - }) - - test('vdom slot > vapor forwarded slot(with fallback) > vapor forwarded slot > vdom slot', async () => { - const foo = ref('foo') - const show = ref(true) - - const VdomSlot = createVdomSlot() - const VaporForwardedSlot2 = createVaporForwardedSlot(VdomSlot) - const VaporForwardedSlot1WithFallback = createVaporForwardedSlot( - VaporForwardedSlot2, - 'vapor1 fallback', - ) - const App = createTestApp(VaporForwardedSlot1WithFallback, foo, show) - - const root = document.createElement('div') - createApp(App).use(vaporInteropPlugin).mount(root) - expect(root.innerHTML).toBe('foo') - - foo.value = 'bar' - await nextTick() - expect(root.innerHTML).toBe('bar') - - show.value = false - await nextTick() - expect(root.innerHTML).toBe('
vapor1 fallback
') - - show.value = true - await nextTick() - expect(root.innerHTML).toBe('bar') - }) - - test('vdom slot > vapor forwarded slot > vapor forwarded slot(with fallback) > vdom slot', async () => { - const foo = ref('foo') - const show = ref(true) - - const VdomSlot = createVdomSlot() - const VaporForwardedSlot2WithFallback = createVaporForwardedSlot( - VdomSlot, - 'vapor2 fallback', - ) - const VaporForwardedSlot1 = createVaporForwardedSlot( - VaporForwardedSlot2WithFallback, - ) - const App = createTestApp(VaporForwardedSlot1, foo, show) - - const root = document.createElement('div') - createApp(App).use(vaporInteropPlugin).mount(root) - expect(root.innerHTML).toBe('foo') - - foo.value = 'bar' - await nextTick() - expect(root.innerHTML).toBe('bar') - - show.value = false - await nextTick() - expect(root.innerHTML).toBe('
vapor2 fallback
') - - show.value = true - await nextTick() - expect(root.innerHTML).toBe('bar') - }) - - test('vdom slot > vapor forwarded slot > vapor forwarded slot > vapor slot', async () => { - const foo = ref('foo') - const show = ref(true) - - const VaporSlot = createVaporSlot() - const VaporForwardedSlot2 = createVaporForwardedSlot(VaporSlot) - const VaporForwardedSlot1 = - createVaporForwardedSlot(VaporForwardedSlot2) - const App = createTestApp(VaporForwardedSlot1, foo, show) - - const root = document.createElement('div') - createApp(App).use(vaporInteropPlugin).mount(root) - expect(root.innerHTML).toBe('foo') - - foo.value = 'bar' - await nextTick() - expect(root.innerHTML).toBe('bar') - - show.value = false - await nextTick() - expect(root.innerHTML).toBe('
fallback
') - - show.value = true - await nextTick() - expect(root.innerHTML).toBe('bar') - }) - - test('vdom slot > vapor forwarded slot(with fallback) > vapor forwarded slot(with fallback) > vdom slot', async () => { - const foo = ref('foo') - const show = ref(true) - - const VdomSlot = createVdomSlot() - const VaporForwardedSlot2WithFallback = createVaporForwardedSlot( - VdomSlot, - 'vapor2 fallback', - ) - const VaporForwardedSlot1WithFallback = createVaporForwardedSlot( - VaporForwardedSlot2WithFallback, - 'vapor1 fallback', - ) - const App = createTestApp(VaporForwardedSlot1WithFallback, foo, show) - - const root = document.createElement('div') - createApp(App).use(vaporInteropPlugin).mount(root) - expect(root.innerHTML).toBe('foo') - - foo.value = 'bar' - await nextTick() - expect(root.innerHTML).toBe('bar') - - show.value = false - await nextTick() - expect(root.innerHTML).toBe('
vapor1 fallback
') - - show.value = true - await nextTick() - expect(root.innerHTML).toBe('bar') - }) - - test('vdom slot > vapor forwarded slot(with fallback) > vapor forwarded slot(with fallback) > vapor slot', async () => { - const foo = ref('foo') - const show = ref(true) - - const VaporSlot = createVaporSlot() - const VaporForwardedSlot2WithFallback = createVaporForwardedSlot( - VaporSlot, - 'vapor2 fallback', - ) - const VaporForwardedSlot1WithFallback = createVaporForwardedSlot( - VaporForwardedSlot2WithFallback, - 'vapor1 fallback', - ) - const App = createTestApp(VaporForwardedSlot1WithFallback, foo, show) - - const root = document.createElement('div') - createApp(App).use(vaporInteropPlugin).mount(root) - expect(root.innerHTML).toBe('foo') - - foo.value = 'bar' - await nextTick() - expect(root.innerHTML).toBe('bar') - - show.value = false - await nextTick() - expect(root.innerHTML).toBe( - '
vapor1 fallback
', - ) - - show.value = true - await nextTick() - expect(root.innerHTML).toBe('bar') - }) - - test('vdom slot > vdom forwarded slot(with fallback) > vdom forwarded slot(with fallback) > vapor slot', async () => { - const foo = ref('foo') - const show = ref(true) - - const VaporSlot = createVaporSlot() - const VdomForwardedSlot2WithFallback = createVdomForwardedSlot( - VaporSlot, - 'vdom2 fallback', - ) - const VdomForwardedSlot1WithFallback = createVdomForwardedSlot( - VdomForwardedSlot2WithFallback, - 'vdom1 fallback', - ) - const App = createTestApp(VdomForwardedSlot1WithFallback, foo, show) - - const root = document.createElement('div') - createApp(App).use(vaporInteropPlugin).mount(root) - expect(root.innerHTML).toBe('foo') - - foo.value = 'bar' - await nextTick() - expect(root.innerHTML).toBe('bar') - - show.value = false - await nextTick() - expect(root.innerHTML).toBe('
vdom1 fallback
') - - show.value = true - await nextTick() - expect(root.innerHTML).toBe('bar') + test('forwarded slot with fallback', async () => { + const Child = defineVaporComponent({ + setup() { + return createSlot('default', null, () => template('child fallback')()) + }, }) - test('vdom slot > vdom forwarded slot(with fallback) > vdom forwarded slot(with fallback) > vdom slot', async () => { - const foo = ref('foo') - const show = ref(true) - - const VdomSlot = createVdomSlot() - const VdomForwardedSlot2WithFallback = createVdomForwardedSlot( - VdomSlot, - 'vdom2 fallback', - ) - const VdomForwardedSlot1WithFallback = createVdomForwardedSlot( - VdomForwardedSlot2WithFallback, - 'vdom1 fallback', - ) - const App = createTestApp(VdomForwardedSlot1WithFallback, foo, show) - - const root = document.createElement('div') - createApp(App).use(vaporInteropPlugin).mount(root) - expect(root.innerHTML).toBe('foo') - - foo.value = 'bar' - await nextTick() - expect(root.innerHTML).toBe('bar') - - show.value = false - await nextTick() - expect(root.innerHTML).toBe('
vdom1 fallback
') - - show.value = true - await nextTick() - expect(root.innerHTML).toBe('bar') + const Parent = defineVaporComponent({ + setup() { + const createForwardedSlot = forwardedSlotCreator() + const n2 = createComponent(Child, null, { + default: () => { + const n0 = createForwardedSlot('default', null, () => { + return template('')() + }) + return n0 + }, + }) + return n2 + }, }) - test('vdom slot > vdom forwarded slot(with fallback) > vdom forwarded slot(with fallback) (multiple) > vapor slot', async () => { - const foo = ref('foo') - const show = ref(true) - - const VaporSlot = createVaporSlot() - const VdomForwardedSlot3WithFallback = createVdomForwardedSlot( - VaporSlot, - 'vdom3 fallback', - ) - const VdomForwardedSlot2WithFallback = createVdomForwardedSlot( - VdomForwardedSlot3WithFallback, - 'vdom2 fallback', - ) - const VdomForwardedSlot1WithFallback = createVdomForwardedSlot( - VdomForwardedSlot2WithFallback, - 'vdom1 fallback', - ) - const App = createTestApp(VdomForwardedSlot1WithFallback, foo, show) - - const root = document.createElement('div') - createApp(App).use(vaporInteropPlugin).mount(root) - expect(root.innerHTML).toBe('foo') - - foo.value = 'bar' - await nextTick() - expect(root.innerHTML).toBe('bar') - - show.value = false - await nextTick() - expect(root.innerHTML).toBe('
vdom1 fallback
') + const { html } = define({ + setup() { + return createComponent(Parent, null, { + default: () => template('')(), + }) + }, + }).render() - show.value = true - await nextTick() - expect(root.innerHTML).toBe('bar') - }) + expect(html()).toBe('child fallback') }) - }) - describe('forwarded slot', () => { - test('should work', async () => { + test('forwarded slot with fallback (v-if)', async () => { const Child = defineVaporComponent({ setup() { - return createSlot('foo', null) + return createSlot('default', null, () => template('child fallback')()) }, }) + + const show = ref(false) const Parent = defineVaporComponent({ setup() { const createForwardedSlot = forwardedSlotCreator() - const n2 = createComponent( - Child, - null, - { - foo: () => { - return createForwardedSlot('foo', null) - }, + const n2 = createComponent(Child, null, { + default: () => { + const n0 = createForwardedSlot('default', null, () => { + const n2 = createIf( + () => show.value, + () => { + const n4 = template('
if content
')() + return n4 + }, + ) + return n2 + }) + return n0 }, - true, - ) + }) return n2 }, }) - const foo = ref('foo') - const { host } = define({ + const { html } = define({ setup() { - const n2 = createComponent( - Parent, - null, - { - foo: () => { - const n0 = template(' ')() as any - renderEffect(() => setText(n0, foo.value)) - return n0 - }, - }, - true, - ) - return n2 + return createComponent(Parent, null, { + default: () => template('')(), + }) }, }).render() - expect(host.innerHTML).toBe('foo') + expect(html()).toBe('child fallback') - foo.value = 'bar' + show.value = true await nextTick() - expect(host.innerHTML).toBe('bar') + expect(html()).toBe( + '
if content
', + ) }) - test('mixed with non-forwarded slot', async () => { + test('forwarded slot with fallback (v-for)', async () => { const Child = defineVaporComponent({ setup() { - return [createSlot('foo', null)] + return createSlot('default', null, () => template('child fallback')()) }, }) + + const items = ref([]) const Parent = defineVaporComponent({ setup() { const createForwardedSlot = forwardedSlotCreator() const n2 = createComponent(Child, null, { - foo: () => { - const n0 = createForwardedSlot('foo', null) + default: () => { + const n0 = createForwardedSlot('default', null, () => { + const n2 = createFor( + () => items.value, + for_item0 => { + const n4 = template(' ')() as any + const x4 = child(n4) as any + renderEffect(() => + setText(x4, toDisplayString(for_item0.value)), + ) + return n4 + }, + ) + return n2 + }) return n0 }, }) - const n3 = createSlot('default', null) - return [n2, n3] + return n2 }, }) - const foo = ref('foo') - const { host } = define({ + const { html } = define({ setup() { - const n2 = createComponent( - Parent, - null, - { - foo: () => { - const n0 = template(' ')() as any - renderEffect(() => setText(n0, foo.value)) - return n0 - }, - default: () => { - const n3 = template(' ')() as any - renderEffect(() => setText(n3, foo.value)) - return n3 - }, - }, - true, - ) - return n2 + return createComponent(Parent, null, { + default: () => template('')(), + }) }, }).render() - expect(host.innerHTML).toBe('foofoo') + expect(html()).toBe('child fallback') - foo.value = 'bar' + items.value.push(1) await nextTick() - expect(host.innerHTML).toBe('barbar') + expect(html()).toBe('1') + + items.value.pop() + await nextTick() + expect(html()).toBe('child fallback') }) describe('vdom interop', () => { diff --git a/packages/runtime-vapor/src/block.ts b/packages/runtime-vapor/src/block.ts index 9ccb0fb26a..87ba58527a 100644 --- a/packages/runtime-vapor/src/block.ts +++ b/packages/runtime-vapor/src/block.ts @@ -102,12 +102,23 @@ export function setFragmentFallback( fragment: VaporFragment, fallback: BlockFn, ): void { - // stop recursion if fragment has its own fallback - if (fragment.fallback) return + if (fragment.fallback) { + const originalFallback = fragment.fallback + // if the original fallback also renders invalid blocks, + // this ensures proper fallback chaining + fragment.fallback = () => { + const fallbackNodes = originalFallback() + if (isValidBlock(fallbackNodes)) { + return fallbackNodes + } + return fallback() + } + } else { + fragment.fallback = fallback + } - fragment.fallback = fallback if (isFragment(fragment.nodes)) { - setFragmentFallback(fragment.nodes, fallback) + setFragmentFallback(fragment.nodes, fragment.fallback) } }