Просмотр исходного кода

feat(runtime-vapor): dynamic component fallback work with dynamic slots (#14064)

edison 5 месяцев назад
Родитель
Сommit
f4b3613f66

+ 31 - 0
packages/runtime-vapor/__tests__/apiCreateDynamicComponent.spec.ts

@@ -148,4 +148,35 @@ describe('api: createDynamicComponent', () => {
     await nextTick()
     expect(html()).toBe('<div><div>B</div><!--dynamic-component--></div>')
   })
+
+  test('fallback with dynamic slots', async () => {
+    const slotName = ref('default')
+    const { html } = define({
+      setup() {
+        return createDynamicComponent(() => 'div', null, {
+          $: [
+            () => ({
+              name: slotName.value,
+              fn: () => template('<span>hi</span>')(),
+            }),
+          ] as any,
+        })
+      },
+    }).render()
+
+    expect(html()).toBe(
+      '<div><span>hi</span><!--slot--></div><!--dynamic-component-->',
+    )
+
+    // update slot name
+    slotName.value = 'custom'
+    await nextTick()
+    expect(html()).toBe('<div><!--slot--></div><!--dynamic-component-->')
+
+    slotName.value = 'default'
+    await nextTick()
+    expect(html()).toBe(
+      '<div><span>hi</span><!--slot--></div><!--dynamic-component-->',
+    )
+  })
 })

+ 28 - 0
packages/runtime-vapor/__tests__/hydration.spec.ts

@@ -1065,6 +1065,34 @@ describe('Vapor Mode hydration', () => {
       `,
       )
     })
+
+    test('dynamic component fallback with dynamic slots', async () => {
+      const data = ref({
+        name: 'default',
+        msg: 'foo',
+      })
+      const { container } = await testHydration(
+        `<template>
+          <component :is="'div'">
+            <template v-slot:[data.name]>
+              <span>{{ data.msg }}</span>
+            </template>
+          </component>
+        </template>`,
+        {},
+        data,
+      )
+
+      expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
+        `"<div><span>foo</span><!----></div><!--dynamic-component-->"`,
+      )
+
+      data.value.msg = 'bar'
+      await nextTick()
+      expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
+        `"<div><span>bar</span><!----></div><!--dynamic-component-->"`,
+      )
+    })
   })
 
   describe('if', () => {

+ 9 - 2
packages/runtime-vapor/src/component.ts

@@ -665,9 +665,16 @@ export function createComponentWithFallback(
       setCurrentHydrationNode(el.firstChild)
     }
     if (rawSlots.$) {
-      // TODO dynamic slot fragment
+      // ssr output does not contain the slot anchor, use an empty string
+      // as the anchor label to avoid slot anchor search errors
+      const frag = new DynamicFragment(
+        isHydrating ? '' : __DEV__ ? 'slot' : undefined,
+      )
+      renderEffect(() => frag.update(getSlot(rawSlots as RawSlots, 'default')))
+      if (!isHydrating) insert(frag, el)
     } else {
-      insert(getSlot(rawSlots as RawSlots, 'default')!(), el)
+      const block = getSlot(rawSlots as RawSlots, 'default')!()
+      if (!isHydrating) insert(block, el)
     }
     if (isHydrating) {
       setCurrentHydrationNode(nextNode)