mirror of https://github.com/vuejs/core.git
fix(ssr): ensure content is valid when rendering normal slot (#11491)
fix #11326
This commit is contained in:
parent
fdc2a31dbd
commit
6c90324870
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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>`)
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue