]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(ssr): ensure content is valid when rendering normal slot (#11491)
authorYuyao Nie <nieyuyao0826@hotmail.com>
Wed, 7 Aug 2024 03:56:00 +0000 (11:56 +0800)
committerGitHub <noreply@github.com>
Wed, 7 Aug 2024 03:56:00 +0000 (11:56 +0800)
fix #11326

packages/runtime-core/src/helpers/renderSlot.ts
packages/runtime-core/src/index.ts
packages/server-renderer/__tests__/ssrSlot.spec.ts
packages/server-renderer/src/helpers/ssrRenderSlot.ts

index f0b13904f08838db5983035985799158966cf78c..d1f8f1d8afe2362f16cdc4bc00a336c27c7b0f22 100644 (file)
@@ -87,7 +87,7 @@ export function renderSlot(
   return rendered
 }
 
-function ensureValidVNode(vnodes: VNodeArrayChildren) {
+export function ensureValidVNode(vnodes: VNodeArrayChildren) {
   return vnodes.some(child => {
     if (!isVNode(child)) return true
     if (child.type === Comment) return false
index 8adbe91ee17c82a591459340f79c1cff8430c8e9..df7b61a6cb6fb16600901345dfe272cae7600b4b 100644 (file)
@@ -371,6 +371,7 @@ import {
 import { renderComponentRoot } from './componentRenderUtils'
 import { setCurrentRenderingInstance } from './componentRenderContext'
 import { isVNode, normalizeVNode } from './vnode'
+import { ensureValidVNode } from './helpers/renderSlot'
 
 const _ssrUtils = {
   createComponentInstance,
@@ -380,6 +381,7 @@ const _ssrUtils = {
   isVNode,
   normalizeVNode,
   getComponentPublicInstance,
+  ensureValidVNode,
 }
 
 /**
index 7e2b2247d2529981a8c0adfaaa719b0be52f44e5..02872274ab60736940175deb199dc03bb25a018e 100644 (file)
@@ -153,4 +153,54 @@ describe('ssr: slot', () => {
       ),
     ).toBe(`<div><p>1</p><p>2</p></div>`)
   })
+
+  // #11326
+  test('dynamic component slot', async () => {
+    expect(
+      await renderToString(
+        createApp({
+          components: {
+            ButtonComp: {
+              template: `<component is="button"><slot/></component>`,
+            },
+            Wrap: {
+              template: `<div><slot/></div>`,
+            },
+          },
+          template: `<ButtonComp><Wrap><div v-if="false">hello</div></Wrap></ButtonComp>`,
+        }),
+      ),
+    ).toBe(`<button><!--[--><div><!--[--><!--]--></div><!--]--></button>`)
+
+    expect(
+      await renderToString(
+        createApp({
+          components: {
+            ButtonComp: {
+              template: `<component is="button"><slot/></component>`,
+            },
+            Wrap: {
+              template: `<div><slot/></div>`,
+            },
+          },
+          template: `<ButtonComp><Wrap><div v-if="true">hello</div></Wrap></ButtonComp>`,
+        }),
+      ),
+    ).toBe(
+      `<button><!--[--><div><!--[--><div>hello</div><!--]--></div><!--]--></button>`,
+    )
+
+    expect(
+      await renderToString(
+        createApp({
+          components: {
+            ButtonComp: {
+              template: `<component is="button"><slot/></component>`,
+            },
+          },
+          template: `<ButtonComp><template v-if="false">hello</template></ButtonComp>`,
+        }),
+      ),
+    ).toBe(`<button><!--[--><!--]--></button>`)
+  })
 })
index 2069591e024ffb606602de4da97c16129a4c394d..06c2d7bfa97801c473476cbfb597bed70d04aab2 100644 (file)
@@ -1,4 +1,4 @@
-import type { ComponentInternalInstance, Slots } from 'vue'
+import { type ComponentInternalInstance, type Slots, ssrUtils } from 'vue'
 import {
   type Props,
   type PushFn,
@@ -7,6 +7,8 @@ import {
 } from '../render'
 import { isArray } from '@vue/shared'
 
+const { ensureValidVNode } = ssrUtils
+
 export type SSRSlots = Record<string, SSRSlot>
 export type SSRSlot = (
   props: Props,
@@ -61,8 +63,18 @@ export function ssrRenderSlotInner(
       slotScopeId ? ' ' + slotScopeId : '',
     )
     if (isArray(ret)) {
-      // normal slot
-      renderVNodeChildren(push, ret, parentComponent, slotScopeId)
+      const validSlotContent = ensureValidVNode(ret)
+      if (validSlotContent) {
+        // normal slot
+        renderVNodeChildren(
+          push,
+          validSlotContent,
+          parentComponent,
+          slotScopeId,
+        )
+      } else if (fallbackRenderFn) {
+        fallbackRenderFn()
+      }
     } else {
       // ssr slot.
       // check if the slot renders all comments, in which case use the fallback