]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(hydration): fix mismatch of leading newline in <textarea> and <pre>
authorEvan You <evan@vuejs.org>
Fri, 13 Sep 2024 13:24:40 +0000 (21:24 +0800)
committerEvan You <evan@vuejs.org>
Fri, 13 Sep 2024 13:24:59 +0000 (21:24 +0800)
close #11873
close #11874

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

index bf07c9773b33414011e3d0147b01f6414364d668..c98f1f473bd9bd9bb0120c6892fa22649930e6d8 100644 (file)
@@ -1908,6 +1908,21 @@ describe('SSR hydration', () => {
       expect(`Hydration attribute mismatch`).toHaveBeenWarned()
     })
 
+    // #11873
+    test('<textarea> with newlines at the beginning', async () => {
+      const render = () => h('textarea', null, '\nhello')
+      const html = await renderToString(createSSRApp({ render }))
+      mountWithHydration(html, render)
+      expect(`Hydration text content mismatch`).not.toHaveBeenWarned()
+    })
+
+    test('<pre> with newlines at the beginning', async () => {
+      const render = () => h('pre', null, '\n')
+      const html = await renderToString(createSSRApp({ render }))
+      mountWithHydration(html, render)
+      expect(`Hydration text content mismatch`).not.toHaveBeenWarned()
+    })
+
     test('boolean attr handling', () => {
       mountWithHydration(`<input />`, () => h('input', { readonly: false }))
       expect(`Hydration attribute mismatch`).not.toHaveBeenWarned()
index b941635b2e9a4c44533fcf3ec09947bd2277e9f9..3cde4377e60090f9d439fcc78fca2cb3ff7ca20c 100644 (file)
@@ -440,7 +440,17 @@ export function createHydrationFunctions(
           remove(cur)
         }
       } else if (shapeFlag & ShapeFlags.TEXT_CHILDREN) {
-        if (el.textContent !== vnode.children) {
+        // #11873 the HTML parser will "eat" the first newline when parsing
+        // <pre> and <textarea>, so if the client value starts with a newline,
+        // we need to remove it before comparing
+        let clientText = vnode.children as string
+        if (
+          clientText[0] === '\n' &&
+          (el.tagName === 'PRE' || el.tagName === 'TEXTAREA')
+        ) {
+          clientText = clientText.slice(1)
+        }
+        if (el.textContent !== clientText) {
           if (!isMismatchAllowed(el, MismatchTypes.TEXT)) {
             ;(__DEV__ || __FEATURE_PROD_HYDRATION_MISMATCH_DETAILS__) &&
               warn(
@@ -753,7 +763,7 @@ export function createHydrationFunctions(
   const isTemplateNode = (node: Node): node is HTMLTemplateElement => {
     return (
       node.nodeType === DOMNodeTypes.ELEMENT &&
-      (node as Element).tagName.toLowerCase() === 'template'
+      (node as Element).tagName === 'TEMPLATE'
     )
   }