Kaynağa Gözat

fix(runtime-vapor): render slot fallback if slot content is not a valid block

close #13668
daiwei 9 ay önce
ebeveyn
işleme
2ba4dc0d07

+ 59 - 0
packages/runtime-vapor/__tests__/componentSlots.spec.ts

@@ -502,5 +502,64 @@ describe('component: slots', () => {
       await nextTick()
       expect(host.innerHTML).toBe('<div><h1></h1><!--slot--></div>')
     })
+
+    test('render fallback when slot content is not valid', async () => {
+      const Child = {
+        setup() {
+          return createSlot('default', null, () =>
+            document.createTextNode('fallback'),
+          )
+        },
+      }
+
+      const { html } = define({
+        setup() {
+          return createComponent(Child, null, {
+            default: () => {
+              return template('<!--comment-->')()
+            },
+          })
+        },
+      }).render()
+
+      expect(html()).toBe('fallback<!--slot-->')
+    })
+
+    test('render fallback when v-if condition is false', async () => {
+      const Child = {
+        setup() {
+          return createSlot('default', null, () =>
+            document.createTextNode('fallback'),
+          )
+        },
+      }
+
+      const toggle = ref(false)
+
+      const { html } = define({
+        setup() {
+          return createComponent(Child, null, {
+            default: () => {
+              return createIf(
+                () => toggle.value,
+                () => {
+                  return document.createTextNode('content')
+                },
+              )
+            },
+          })
+        },
+      }).render()
+
+      expect(html()).toBe('fallback<!--if--><!--slot-->')
+
+      toggle.value = true
+      await nextTick()
+      expect(html()).toBe('content<!--if--><!--slot-->')
+
+      toggle.value = false
+      await nextTick()
+      expect(html()).toBe('fallback<!--if--><!--slot-->')
+    })
   })
 })

+ 9 - 3
packages/runtime-vapor/src/block.ts

@@ -67,9 +67,15 @@ export class DynamicFragment extends VaporFragment {
 
     if (this.fallback && !isValidBlock(this.nodes)) {
       parent && remove(this.nodes, parent)
-      this.nodes =
-        (this.scope || (this.scope = new EffectScope())).run(this.fallback) ||
-        []
+      // if current nodes is a DynamicFragment, call its update with the fallback
+      // to handle nested dynamic fragment
+      if (this.nodes instanceof DynamicFragment) {
+        this.nodes.update(this.fallback)
+      } else {
+        this.nodes =
+          (this.scope || (this.scope = new EffectScope())).run(this.fallback) ||
+          []
+      }
       parent && insert(this.nodes, parent, this.anchor)
     }
 

+ 1 - 0
packages/runtime-vapor/src/componentSlots.ts

@@ -126,6 +126,7 @@ export function createSlot(
     const renderSlot = () => {
       const slot = getSlot(rawSlots, isFunction(name) ? name() : name)
       if (slot) {
+        fragment.fallback = fallback
         // create and cache bound version of the slot to make it stable
         // so that we avoid unnecessary updates if it resolves to the same slot
         fragment.update(