]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(hydration): fix tagName access eeror on comment/text node hydration mismatch
authorEvan You <yyx990803@gmail.com>
Mon, 6 Nov 2023 10:45:42 +0000 (18:45 +0800)
committerEvan You <yyx990803@gmail.com>
Mon, 6 Nov 2023 10:45:42 +0000 (18:45 +0800)
fix #9531

packages/runtime-core/__tests__/hydration.spec.ts
packages/runtime-core/src/hydration.ts

index 759804b97f1bc3337ee34fabfe2cd8fb4720ae7b..abf50c54668fa0fc57e6c3af12e3c45afc46c5b9 100644 (file)
@@ -75,6 +75,16 @@ describe('SSR hydration', () => {
     expect(vnode.el.nodeType).toBe(8) // comment
   })
 
+  test('comment (real left square bracket)', () => {
+    const { vnode, container } = mountWithHydration(
+      `<div><span>foo</span><!--hello--></div>`,
+      () => h('div', [h('span', 'foo'), createCommentVNode('hello')])
+    )
+    expect(vnode.el).toBe(container.firstChild)
+    expect(container.innerHTML).toBe('<div><span>foo</span><!--hello--></div>')
+    expect(`Hydration node mismatch`).not.toHaveBeenWarned()
+  })
+
   test('static', () => {
     const html = '<div><span>hello</span></div>'
     const { vnode, container } = mountWithHydration(html, () =>
@@ -1177,5 +1187,21 @@ describe('SSR hydration', () => {
       expect(teleportContainer.innerHTML).toBe(`<span>value</span>`)
       expect(`Hydration children mismatch`).toHaveBeenWarned()
     })
+
+    test('comment mismatch (element)', () => {
+      const { container } = mountWithHydration(`<div><span></span></div>`, () =>
+        h('div', [createCommentVNode('hi')])
+      )
+      expect(container.innerHTML).toBe('<div><!--hi--></div>')
+      expect(`Hydration node mismatch`).toHaveBeenWarned()
+    })
+
+    test('comment mismatch (text)', () => {
+      const { container } = mountWithHydration(`<div>foobar</div>`, () =>
+        h('div', [createCommentVNode('hi')])
+      )
+      expect(container.innerHTML).toBe('<div><!--hi--></div>')
+      expect(`Hydration node mismatch`).toHaveBeenWarned()
+    })
   })
 })
index 4e91cb3d1cbc3f5ef89977ca718c1b4b3862cf77..b8940e6679a8d547cfeac8465af3a05f0f1b0d51 100644 (file)
@@ -145,18 +145,17 @@ export function createHydrationFunctions(
         }
         break
       case Comment:
-        if (domType !== DOMNodeTypes.COMMENT || isFragmentStart) {
-          if ((node as Element).tagName.toLowerCase() === 'template') {
-            const content = (vnode.el! as HTMLTemplateElement).content
-              .firstChild!
-
-            // replace <template> node with inner children
-            replaceNode(content, node, parentComponent)
-            vnode.el = node = content
-            nextNode = nextSibling(node)
-          } else {
-            nextNode = onMismatch()
-          }
+        if (isTemplateNode(node)) {
+          nextNode = nextSibling(node)
+          // wrapped <transition appear>
+          // replace <template> node with inner child
+          replaceNode(
+            (vnode.el = node.content.firstChild!),
+            node,
+            parentComponent
+          )
+        } else if (domType !== DOMNodeTypes.COMMENT || isFragmentStart) {
+          nextNode = onMismatch()
         } else {
           nextNode = nextSibling(node)
         }
@@ -209,7 +208,7 @@ export function createHydrationFunctions(
             (domType !== DOMNodeTypes.ELEMENT ||
               (vnode.type as string).toLowerCase() !==
                 (node as Element).tagName.toLowerCase()) &&
-            !isTemplateNode(node as Element)
+            !isTemplateNode(node)
           ) {
             nextNode = onMismatch()
           } else {
@@ -637,17 +636,16 @@ export function createHydrationFunctions(
     let parent = parentComponent
     while (parent) {
       if (parent.vnode.el === oldNode) {
-        parent.vnode.el = newNode
-        parent.subTree.el = newNode
+        parent.vnode.el = parent.subTree.el = newNode
       }
       parent = parent.parent
     }
   }
 
-  const isTemplateNode = (node: Element): boolean => {
+  const isTemplateNode = (node: Node): node is HTMLTemplateElement => {
     return (
       node.nodeType === DOMNodeTypes.ELEMENT &&
-      node.tagName.toLowerCase() === 'template'
+      (node as Element).tagName.toLowerCase() === 'template'
     )
   }