Skip to content

Commit ce4915d

Browse files
authored
fix(slots): should render fallback content when slot content contains no valid nodes (#2485)
fix #2347, fix #2461
1 parent cf7f1db commit ce4915d

File tree

2 files changed

+51
-6
lines changed

2 files changed

+51
-6
lines changed

packages/runtime-core/__tests__/helpers/renderSlot.spec.ts

+29-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ import {
55
createVNode,
66
openBlock,
77
createBlock,
8-
Fragment
8+
Fragment,
9+
createCommentVNode
910
} from '../../src'
1011
import { PatchFlags } from '@vue/shared/src'
1112

@@ -47,4 +48,31 @@ describe('renderSlot', () => {
4748
const templateRendered = renderSlot({ default: slot }, 'default')
4849
expect(templateRendered.dynamicChildren!.length).toBe(1)
4950
})
51+
52+
// #2347 #2461
53+
describe('only render valid slot content', () => {
54+
it('should ignore slots that are all comments', () => {
55+
let fallback
56+
const vnode = renderSlot(
57+
{ default: () => [createCommentVNode('foo')] },
58+
'default',
59+
undefined,
60+
() => [(fallback = h('fallback'))]
61+
)
62+
expect(vnode.children).toEqual([fallback])
63+
expect(vnode.patchFlag).toBe(PatchFlags.BAIL)
64+
})
65+
66+
it('should ignore invalid slot content generated by nested slot', () => {
67+
let fallback
68+
const vnode = renderSlot(
69+
{ default: () => [renderSlot({}, 'foo')] },
70+
'default',
71+
undefined,
72+
() => [(fallback = h('fallback'))]
73+
)
74+
expect(vnode.children).toEqual([fallback])
75+
expect(vnode.patchFlag).toBe(PatchFlags.BAIL)
76+
})
77+
})
5078
})

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

+22-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Data } from '../component'
22
import { Slots, RawSlots } from '../componentSlots'
3+
import { Comment, isVNode } from '../vnode'
34
import {
45
VNodeArrayChildren,
56
openBlock,
@@ -42,15 +43,31 @@ export function renderSlot(
4243
// `renderSlot` we can be sure that it's template-based so we can force
4344
// enable it.
4445
isRenderingCompiledSlot++
45-
const rendered = (openBlock(),
46-
createBlock(
46+
openBlock()
47+
const validSlotContent = slot && ensureValidVNode(slot(props))
48+
const rendered = createBlock(
4749
Fragment,
4850
{ key: props.key },
49-
slot ? slot(props) : fallback ? fallback() : [],
50-
(slots as RawSlots)._ === SlotFlags.STABLE
51+
validSlotContent || (fallback ? fallback() : []),
52+
validSlotContent && (slots as RawSlots)._ === SlotFlags.STABLE
5153
? PatchFlags.STABLE_FRAGMENT
5254
: PatchFlags.BAIL
53-
))
55+
)
5456
isRenderingCompiledSlot--
5557
return rendered
5658
}
59+
60+
function ensureValidVNode(vnodes: VNodeArrayChildren) {
61+
return vnodes.some(child => {
62+
if (!isVNode(child)) return true
63+
if (child.type === Comment) return false
64+
if (
65+
child.type === Fragment &&
66+
!ensureValidVNode(child.children as VNodeArrayChildren)
67+
)
68+
return false
69+
return true
70+
})
71+
? vnodes
72+
: null
73+
}

0 commit comments

Comments
 (0)