]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(slots): should render fallback content when slot content contains no valid nodes...
authorHcySunYang <HcySunYang@outlook.com>
Thu, 26 Nov 2020 16:35:45 +0000 (00:35 +0800)
committerGitHub <noreply@github.com>
Thu, 26 Nov 2020 16:35:45 +0000 (11:35 -0500)
fix #2347, fix #2461

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

index e14f4b75025164bb1b5ea77114be8624c9fa37f7..99f2292b771cd23744f72e17b97732e047b14d43 100644 (file)
@@ -5,7 +5,8 @@ import {
   createVNode,
   openBlock,
   createBlock,
-  Fragment
+  Fragment,
+  createCommentVNode
 } from '../../src'
 import { PatchFlags } from '@vue/shared/src'
 
@@ -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)
+    })
+  })
 })
index 2e43c628678bbcc450b57f265ed23a096ae4a416..c20b8a191dc9599231a4328aabc5d3912b33f769 100644 (file)
@@ -1,5 +1,6 @@
 import { Data } from '../component'
 import { Slots, RawSlots } from '../componentSlots'
+import { Comment, isVNode } from '../vnode'
 import {
   VNodeArrayChildren,
   openBlock,
@@ -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
+}