fix(slots): should render fallback content when slot content contains no valid nodes (#2485)

fix #2347, fix #2461
This commit is contained in:
HcySunYang 2020-11-27 00:35:45 +08:00 committed by GitHub
parent cf7f1dbc9b
commit ce4915d8be
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 51 additions and 6 deletions

View File

@ -5,7 +5,8 @@ import {
createVNode, createVNode,
openBlock, openBlock,
createBlock, createBlock,
Fragment Fragment,
createCommentVNode
} from '../../src' } from '../../src'
import { PatchFlags } from '@vue/shared/src' import { PatchFlags } from '@vue/shared/src'
@ -47,4 +48,31 @@ describe('renderSlot', () => {
const templateRendered = renderSlot({ default: slot }, 'default') const templateRendered = renderSlot({ default: slot }, 'default')
expect(templateRendered.dynamicChildren!.length).toBe(1) 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)
})
})
}) })

View File

@ -1,5 +1,6 @@
import { Data } from '../component' import { Data } from '../component'
import { Slots, RawSlots } from '../componentSlots' import { Slots, RawSlots } from '../componentSlots'
import { Comment, isVNode } from '../vnode'
import { import {
VNodeArrayChildren, VNodeArrayChildren,
openBlock, openBlock,
@ -42,15 +43,31 @@ export function renderSlot(
// `renderSlot` we can be sure that it's template-based so we can force // `renderSlot` we can be sure that it's template-based so we can force
// enable it. // enable it.
isRenderingCompiledSlot++ isRenderingCompiledSlot++
const rendered = (openBlock(), openBlock()
createBlock( const validSlotContent = slot && ensureValidVNode(slot(props))
const rendered = createBlock(
Fragment, Fragment,
{ key: props.key }, { key: props.key },
slot ? slot(props) : fallback ? fallback() : [], validSlotContent || (fallback ? fallback() : []),
(slots as RawSlots)._ === SlotFlags.STABLE validSlotContent && (slots as RawSlots)._ === SlotFlags.STABLE
? PatchFlags.STABLE_FRAGMENT ? PatchFlags.STABLE_FRAGMENT
: PatchFlags.BAIL : PatchFlags.BAIL
)) )
isRenderingCompiledSlot-- isRenderingCompiledSlot--
return rendered 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
}