fix(ssr): ensure content is valid when rendering normal slot (#11491)

fix #11326
This commit is contained in:
Yuyao Nie 2024-08-07 11:56:00 +08:00 committed by GitHub
parent fdc2a31dbd
commit 6c90324870
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 68 additions and 4 deletions

View File

@ -87,7 +87,7 @@ export function renderSlot(
return rendered
}
function ensureValidVNode(vnodes: VNodeArrayChildren) {
export function ensureValidVNode(vnodes: VNodeArrayChildren) {
return vnodes.some(child => {
if (!isVNode(child)) return true
if (child.type === Comment) return false

View File

@ -371,6 +371,7 @@ import {
import { renderComponentRoot } from './componentRenderUtils'
import { setCurrentRenderingInstance } from './componentRenderContext'
import { isVNode, normalizeVNode } from './vnode'
import { ensureValidVNode } from './helpers/renderSlot'
const _ssrUtils = {
createComponentInstance,
@ -380,6 +381,7 @@ const _ssrUtils = {
isVNode,
normalizeVNode,
getComponentPublicInstance,
ensureValidVNode,
}
/**

View File

@ -153,4 +153,54 @@ describe('ssr: slot', () => {
),
).toBe(`<div><p>1</p><p>2</p></div>`)
})
// #11326
test('dynamic component slot', async () => {
expect(
await renderToString(
createApp({
components: {
ButtonComp: {
template: `<component is="button"><slot/></component>`,
},
Wrap: {
template: `<div><slot/></div>`,
},
},
template: `<ButtonComp><Wrap><div v-if="false">hello</div></Wrap></ButtonComp>`,
}),
),
).toBe(`<button><!--[--><div><!--[--><!--]--></div><!--]--></button>`)
expect(
await renderToString(
createApp({
components: {
ButtonComp: {
template: `<component is="button"><slot/></component>`,
},
Wrap: {
template: `<div><slot/></div>`,
},
},
template: `<ButtonComp><Wrap><div v-if="true">hello</div></Wrap></ButtonComp>`,
}),
),
).toBe(
`<button><!--[--><div><!--[--><div>hello</div><!--]--></div><!--]--></button>`,
)
expect(
await renderToString(
createApp({
components: {
ButtonComp: {
template: `<component is="button"><slot/></component>`,
},
},
template: `<ButtonComp><template v-if="false">hello</template></ButtonComp>`,
}),
),
).toBe(`<button><!--[--><!--]--></button>`)
})
})

View File

@ -1,4 +1,4 @@
import type { ComponentInternalInstance, Slots } from 'vue'
import { type ComponentInternalInstance, type Slots, ssrUtils } from 'vue'
import {
type Props,
type PushFn,
@ -7,6 +7,8 @@ import {
} from '../render'
import { isArray } from '@vue/shared'
const { ensureValidVNode } = ssrUtils
export type SSRSlots = Record<string, SSRSlot>
export type SSRSlot = (
props: Props,
@ -61,8 +63,18 @@ export function ssrRenderSlotInner(
slotScopeId ? ' ' + slotScopeId : '',
)
if (isArray(ret)) {
// normal slot
renderVNodeChildren(push, ret, parentComponent, slotScopeId)
const validSlotContent = ensureValidVNode(ret)
if (validSlotContent) {
// normal slot
renderVNodeChildren(
push,
validSlotContent,
parentComponent,
slotScopeId,
)
} else if (fallbackRenderFn) {
fallbackRenderFn()
}
} else {
// ssr slot.
// check if the slot renders all comments, in which case use the fallback