Skip to content

Commit 9daab07

Browse files
committed
fix(ssrRenderSlot): ensure content is valid when rendering slot
1 parent 79602f9 commit 9daab07

File tree

4 files changed

+57
-3
lines changed

4 files changed

+57
-3
lines changed

packages/runtime-core/src/helpers/renderSlot.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ export function renderSlot(
8787
return rendered
8888
}
8989

90-
function ensureValidVNode(vnodes: VNodeArrayChildren) {
90+
export function ensureValidVNode(vnodes: VNodeArrayChildren) {
9191
return vnodes.some(child => {
9292
if (!isVNode(child)) return true
9393
if (child.type === Comment) return false

packages/runtime-core/src/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,7 @@ import {
371371
import { renderComponentRoot } from './componentRenderUtils'
372372
import { setCurrentRenderingInstance } from './componentRenderContext'
373373
import { isVNode, normalizeVNode } from './vnode'
374+
import { ensureValidVNode } from './helpers/renderSlot'
374375

375376
const _ssrUtils = {
376377
createComponentInstance,
@@ -380,6 +381,7 @@ const _ssrUtils = {
380381
isVNode,
381382
normalizeVNode,
382383
getComponentPublicInstance,
384+
ensureValidVNode,
383385
}
384386

385387
/**

packages/server-renderer/__tests__/ssrSlot.spec.ts

+50
Original file line numberDiff line numberDiff line change
@@ -153,4 +153,54 @@ describe('ssr: slot', () => {
153153
),
154154
).toBe(`<div><p>1</p><p>2</p></div>`)
155155
})
156+
157+
// #11326
158+
test('dynamic component slot', async () => {
159+
expect(
160+
await renderToString(
161+
createApp({
162+
components: {
163+
ButtonComp: {
164+
template: `<component is="button"><slot/></component>`,
165+
},
166+
Wrap: {
167+
template: `<div><slot/></div>`,
168+
},
169+
},
170+
template: `<ButtonComp><Wrap><div v-if="false">hello</div></Wrap></ButtonComp>`,
171+
}),
172+
),
173+
).toBe(`<button><!--[--><div><!--[--><!--]--></div><!--]--></button>`)
174+
175+
expect(
176+
await renderToString(
177+
createApp({
178+
components: {
179+
ButtonComp: {
180+
template: `<component is="button"><slot/></component>`,
181+
},
182+
Wrap: {
183+
template: `<div><slot/></div>`,
184+
},
185+
},
186+
template: `<ButtonComp><Wrap><div v-if="true">hello</div></Wrap></ButtonComp>`,
187+
}),
188+
),
189+
).toBe(
190+
`<button><!--[--><div><!--[--><div>hello</div><!--]--></div><!--]--></button>`,
191+
)
192+
193+
expect(
194+
await renderToString(
195+
createApp({
196+
components: {
197+
ButtonComp: {
198+
template: `<component is="button"><slot/></component>`,
199+
},
200+
},
201+
template: `<ButtonComp><template v-if="false">hello</template></ButtonComp>`,
202+
}),
203+
),
204+
).toBe(`<button><!--[--><!--]--></button>`)
205+
})
156206
})

packages/server-renderer/src/helpers/ssrRenderSlot.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { ComponentInternalInstance, Slots } from 'vue'
1+
import { type ComponentInternalInstance, type Slots, ssrUtils } from 'vue'
22
import {
33
type Props,
44
type PushFn,
@@ -7,6 +7,8 @@ import {
77
} from '../render'
88
import { isArray } from '@vue/shared'
99

10+
const { ensureValidVNode } = ssrUtils
11+
1012
export type SSRSlots = Record<string, SSRSlot>
1113
export type SSRSlot = (
1214
props: Props,
@@ -60,7 +62,7 @@ export function ssrRenderSlotInner(
6062
parentComponent,
6163
slotScopeId ? ' ' + slotScopeId : '',
6264
)
63-
if (isArray(ret)) {
65+
if (isArray(ret) && ensureValidVNode(ret)) {
6466
// normal slot
6567
renderVNodeChildren(push, ret, parentComponent, slotScopeId)
6668
} else {

0 commit comments

Comments
 (0)