Skip to content

Commit

Permalink
fix(slots): should render fallback content when slot content contains…
Browse files Browse the repository at this point in the history
… no valid nodes (#2485)

fix #2347, fix #2461
  • Loading branch information
HcySunYang authored Nov 26, 2020
1 parent cf7f1db commit ce4915d
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 6 deletions.
30 changes: 29 additions & 1 deletion packages/runtime-core/__tests__/helpers/renderSlot.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import {
createVNode,
openBlock,
createBlock,
Fragment
Fragment,
createCommentVNode
} from '../../src'
import { PatchFlags } from '@vue/shared/src'

Expand Down Expand Up @@ -47,4 +48,31 @@ describe('renderSlot', () => {
const templateRendered = renderSlot({ default: slot }, 'default')
expect(templateRendered.dynamicChildren!.length).toBe(1)
})

// #2347 #2461
describe('only render valid slot content', () => {
it('should ignore slots that are all comments', () => {
let fallback
const vnode = renderSlot(
{ default: () => [createCommentVNode('foo')] },
'default',
undefined,
() => [(fallback = h('fallback'))]
)
expect(vnode.children).toEqual([fallback])
expect(vnode.patchFlag).toBe(PatchFlags.BAIL)
})

it('should ignore invalid slot content generated by nested slot', () => {
let fallback
const vnode = renderSlot(
{ default: () => [renderSlot({}, 'foo')] },
'default',
undefined,
() => [(fallback = h('fallback'))]
)
expect(vnode.children).toEqual([fallback])
expect(vnode.patchFlag).toBe(PatchFlags.BAIL)
})
})
})
27 changes: 22 additions & 5 deletions packages/runtime-core/src/helpers/renderSlot.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Data } from '../component'
import { Slots, RawSlots } from '../componentSlots'
import { Comment, isVNode } from '../vnode'
import {
VNodeArrayChildren,
openBlock,
Expand Down Expand Up @@ -42,15 +43,31 @@ export function renderSlot(
// `renderSlot` we can be sure that it's template-based so we can force
// enable it.
isRenderingCompiledSlot++
const rendered = (openBlock(),
createBlock(
openBlock()
const validSlotContent = slot && ensureValidVNode(slot(props))
const rendered = createBlock(
Fragment,
{ key: props.key },
slot ? slot(props) : fallback ? fallback() : [],
(slots as RawSlots)._ === SlotFlags.STABLE
validSlotContent || (fallback ? fallback() : []),
validSlotContent && (slots as RawSlots)._ === SlotFlags.STABLE
? PatchFlags.STABLE_FRAGMENT
: PatchFlags.BAIL
))
)
isRenderingCompiledSlot--
return rendered
}

function ensureValidVNode(vnodes: VNodeArrayChildren) {
return vnodes.some(child => {
if (!isVNode(child)) return true
if (child.type === Comment) return false
if (
child.type === Fragment &&
!ensureValidVNode(child.children as VNodeArrayChildren)
)
return false
return true
})
? vnodes
: null
}

0 comments on commit ce4915d

Please sign in to comment.