2
0
Эх сурвалжийг харах

fix(ssr): ensure content is valid when rendering normal slot (#11491)

fix #11326
Yuyao Nie 1 жил өмнө
parent
commit
6c90324870

+ 1 - 1
packages/runtime-core/src/helpers/renderSlot.ts

@@ -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

+ 2 - 0
packages/runtime-core/src/index.ts

@@ -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,
 }
 
 /**

+ 50 - 0
packages/server-renderer/__tests__/ssrSlot.spec.ts

@@ -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>`)
+  })
 })

+ 15 - 3
packages/server-renderer/src/helpers/ssrRenderSlot.ts

@@ -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