瀏覽代碼

fix(ssr): fix hydration for slot with empty text node

fix #5728
Evan You 4 年之前
父節點
當前提交
939209c6b5
共有 2 個文件被更改,包括 32 次插入2 次删除
  1. 20 1
      packages/runtime-core/__tests__/hydration.spec.ts
  2. 12 1
      packages/runtime-core/src/hydration.ts

+ 20 - 1
packages/runtime-core/__tests__/hydration.spec.ts

@@ -13,7 +13,8 @@ import {
   createTextVNode,
   createVNode,
   withDirectives,
-  vModelCheckbox
+  vModelCheckbox,
+  renderSlot
 } from '@vue/runtime-dom'
 import { renderToString, SSRContext } from '@vue/server-renderer'
 import { PatchFlags } from '../../shared/src'
@@ -912,6 +913,24 @@ describe('SSR hydration', () => {
     expect((container.firstChild!.firstChild as any)._value).toBe(true)
   })
 
+  // #5728
+  test('empty text node in slot', () => {
+    const Comp = {
+      render(this: any) {
+        return renderSlot(this.$slots, 'default', {}, () => [
+          createTextVNode('')
+        ])
+      }
+    }
+    const { container, vnode } = mountWithHydration('<!--[--><!--]-->', () => h(Comp))
+    expect(container.childNodes.length).toBe(3)
+    const text = container.childNodes[1]
+    expect(text.nodeType).toBe(3)
+    expect(vnode.el).toBe(container.childNodes[0])
+    // component => slot fragment => text node
+    expect((vnode as any).component?.subTree.children[0].el).toBe(text)
+  })
+
   describe('mismatch handling', () => {
     test('text node', () => {
       const { container } = mountWithHydration(`foo`, () => 'bar')

+ 12 - 1
packages/runtime-core/src/hydration.ts

@@ -110,7 +110,18 @@ export function createHydrationFunctions(
     switch (type) {
       case Text:
         if (domType !== DOMNodeTypes.TEXT) {
-          nextNode = onMismatch()
+          // #5728 empty text node inside a slot can cause hydration failure
+          // because the server rendered HTML won't contain a text node
+          if (vnode.children === '') {
+            insert(
+              (vnode.el = document.createTextNode('')),
+              node.parentElement!,
+              node
+            )
+            nextNode = node
+          } else {
+            nextNode = onMismatch()
+          }
         } else {
           if ((node as Text).data !== vnode.children) {
             hasMismatch = true