Skip to content

Commit

Permalink
fix(ssr): ensure content is valid when rendering normal slot (#11491)
Browse files Browse the repository at this point in the history
fix #11326
  • Loading branch information
nieyuyao authored Aug 7, 2024
1 parent fdc2a31 commit 6c90324
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 4 deletions.
2 changes: 1 addition & 1 deletion packages/runtime-core/src/helpers/renderSlot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions packages/runtime-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -380,6 +381,7 @@ const _ssrUtils = {
isVNode,
normalizeVNode,
getComponentPublicInstance,
ensureValidVNode,
}

/**
Expand Down
50 changes: 50 additions & 0 deletions packages/server-renderer/__tests__/ssrSlot.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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>`)
})
})
18 changes: 15 additions & 3 deletions packages/server-renderer/src/helpers/ssrRenderSlot.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { ComponentInternalInstance, Slots } from 'vue'
import { type ComponentInternalInstance, type Slots, ssrUtils } from 'vue'
import {
type Props,
type PushFn,
Expand All @@ -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,
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 6c90324

Please sign in to comment.