fix(runtime-core): force diff slot fallback content and provided content

fix #7256
fix #9200
fix #9308

close #7266
close #9213
This commit is contained in:
Evan You 2024-07-12 18:22:31 +08:00
parent 685e3f381c
commit d76dd9c58d
No known key found for this signature in database
GPG Key ID: 00E9AB7A6704CE0A
3 changed files with 85 additions and 6 deletions

View File

@ -26,13 +26,17 @@ describe('renderSlot', () => {
const vnode = renderSlot(
{ default: () => [(child = h('child'))] },
'default',
{ key: 'foo' },
)
expect(vnode.children).toEqual([child])
expect(vnode.key).toBe('foo')
})
it('should render slot fallback', () => {
const vnode = renderSlot({}, 'default', {}, () => ['fallback'])
const vnode = renderSlot({}, 'default', { key: 'foo' }, () => ['fallback'])
expect(vnode.children).toEqual(['fallback'])
// should attach fallback key postfix
expect(vnode.key).toBe('foo_fb')
})
it('should warn render ssr slot', () => {

View File

@ -1105,4 +1105,77 @@ describe('renderer: optimized mode', () => {
expect(app.config.errorHandler).not.toHaveBeenCalled()
}
})
test('diff slot and slot fallback node', async () => {
const Comp = {
props: ['show'],
setup(props: any, { slots }: SetupContext) {
return () => {
return (
openBlock(),
createElementBlock('div', null, [
renderSlot(slots, 'default', { hide: !props.show }, () => [
(openBlock(),
(block = createElementBlock(
Fragment,
{ key: 0 },
[createTextVNode('foo')],
PatchFlags.STABLE_FRAGMENT,
))),
]),
])
)
}
},
}
const show = ref(true)
const app = createApp({
render() {
return (
openBlock(),
createBlock(
Comp,
{ show: show.value },
{
default: withCtx(({ hide }: { hide: boolean }) => [
!hide
? (openBlock(),
createElementBlock(
Fragment,
{ key: 0 },
[
createCommentVNode('comment'),
createElementVNode(
'div',
null,
'bar',
PatchFlags.HOISTED,
),
],
PatchFlags.STABLE_FRAGMENT,
))
: createCommentVNode('v-if', true),
]),
_: SlotFlags.STABLE,
},
PatchFlags.PROPS,
['show'],
)
)
},
})
app.mount(root)
expect(inner(root)).toBe('<div><!--comment--><div>bar</div></div>')
expect(block).toBe(null)
show.value = false
await nextTick()
expect(inner(root)).toBe('<div>foo</div>')
show.value = true
await nextTick()
expect(inner(root)).toBe('<div><!--comment--><div>bar</div></div>')
})
})

View File

@ -65,11 +65,13 @@ export function renderSlot(
Fragment,
{
key:
props.key ||
// slot content array of a dynamic conditional slot may have a branch
// key attached in the `createSlots` helper, respect that
(validSlotContent && (validSlotContent as any).key) ||
`_${name}`,
(props.key ||
// slot content array of a dynamic conditional slot may have a branch
// key attached in the `createSlots` helper, respect that
(validSlotContent && (validSlotContent as any).key) ||
`_${name}`) +
// #7256 force differentiate fallback content from actual content
(!validSlotContent && fallback ? '_fb' : ''),
},
validSlotContent || (fallback ? fallback() : []),
validSlotContent && (slots as RawSlots)._ === SlotFlags.STABLE