]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(hydration): do not warn against bindings w/ object values
authorEvan You <yyx990803@gmail.com>
Thu, 11 Jan 2024 08:18:00 +0000 (16:18 +0800)
committerEvan You <yyx990803@gmail.com>
Thu, 11 Jan 2024 08:18:00 +0000 (16:18 +0800)
packages/runtime-core/__tests__/hydration.spec.ts
packages/runtime-core/src/hydration.ts

index cd0c61982b8b18d7ae710fcd478815da82c5d7d5..17d7b8dbcee30f106a2a12eb89872fce15beee15 100644 (file)
@@ -1080,13 +1080,11 @@ describe('SSR hydration', () => {
   })
 
   test('force hydrate prop with `.prop` modifier', () => {
-    const { container } = mountWithHydration(
-      '<input type="checkbox" :indeterminate.prop="true">',
-      () =>
-        h('input', {
-          type: 'checkbox',
-          '.indeterminate': true,
-        }),
+    const { container } = mountWithHydration('<input type="checkbox">', () =>
+      h('input', {
+        type: 'checkbox',
+        '.indeterminate': true,
+      }),
     )
     expect((container.firstChild! as any).indeterminate).toBe(true)
   })
@@ -1475,6 +1473,16 @@ describe('SSR hydration', () => {
       mountWithHydration(`<select multiple></div>`, () =>
         h('select', { multiple: 'multiple' }),
       )
+      expect(`Hydration attribute mismatch`).not.toHaveBeenWarned()
+
+      mountWithHydration(`<div></div>`, () => h('div', { id: 'foo' }))
+      expect(`Hydration attribute mismatch`).toHaveBeenWarnedTimes(1)
+
+      mountWithHydration(`<div id="bar"></div>`, () => h('div', { id: 'foo' }))
+      expect(`Hydration attribute mismatch`).toHaveBeenWarnedTimes(2)
+    })
+
+    test('attr special case: textarea value', () => {
       mountWithHydration(`<textarea>foo</textarea>`, () =>
         h('textarea', { value: 'foo' }),
       )
@@ -1483,11 +1491,10 @@ describe('SSR hydration', () => {
       )
       expect(`Hydration attribute mismatch`).not.toHaveBeenWarned()
 
-      mountWithHydration(`<div></div>`, () => h('div', { id: 'foo' }))
+      mountWithHydration(`<textarea>foo</textarea>`, () =>
+        h('textarea', { value: 'bar' }),
+      )
       expect(`Hydration attribute mismatch`).toHaveBeenWarned()
-
-      mountWithHydration(`<div id="bar"></div>`, () => h('div', { id: 'foo' }))
-      expect(`Hydration attribute mismatch`).toHaveBeenWarnedTimes(2)
     })
 
     test('boolean attr handling', () => {
@@ -1504,5 +1511,10 @@ describe('SSR hydration', () => {
       )
       expect(`Hydration attribute mismatch`).not.toHaveBeenWarned()
     })
+
+    test('should not warn against object values', () => {
+      mountWithHydration(`<input />`, () => h('input', { from: {} }))
+      expect(`Hydration attribute mismatch`).not.toHaveBeenWarned()
+    })
   })
 })
index 8f095f6167f4859389bc54423a927d72ef1e321e..4df4eecece93d5cdf6cfe56289940880c380f78e 100644 (file)
@@ -21,6 +21,7 @@ import {
   isBooleanAttr,
   isKnownHtmlAttr,
   isKnownSvgAttr,
+  isObject,
   isOn,
   isReservedProp,
   isString,
@@ -759,12 +760,17 @@ function propHasMismatch(
       expected = includeBooleanAttr(clientValue)
     } else {
       // #10000 some attrs such as textarea.value can't be get by `hasAttribute`
-      actual = el.hasAttribute(key)
-        ? el.getAttribute(key)
-        : key in el
-          ? el[key as keyof typeof el]
-          : ''
-      expected = clientValue == null ? '' : String(clientValue)
+      if (el.hasAttribute(key)) {
+        actual = el.getAttribute(key)
+      } else if (key in el) {
+        const serverValue = el[key as keyof typeof el]
+        if (!isObject(serverValue)) {
+          actual = serverValue == null ? '' : String(serverValue)
+        }
+      }
+      if (!isObject(clientValue)) {
+        expected = clientValue == null ? '' : String(clientValue)
+      }
     }
     if (actual !== expected) {
       mismatchType = `attribute`
@@ -775,15 +781,20 @@ function propHasMismatch(
   if (mismatchType) {
     const format = (v: any) =>
       v === false ? `(not rendered)` : `${mismatchKey}="${v}"`
-    warn(
-      `Hydration ${mismatchType} mismatch on`,
-      el,
+    const preSegment = `Hydration ${mismatchType} mismatch on`
+    const postSegment =
       `\n  - rendered on server: ${format(actual)}` +
-        `\n  - expected on client: ${format(expected)}` +
-        `\n  Note: this mismatch is check-only. The DOM will not be rectified ` +
-        `in production due to performance overhead.` +
-        `\n  You should fix the source of the mismatch.`,
-    )
+      `\n  - expected on client: ${format(expected)}` +
+      `\n  Note: this mismatch is check-only. The DOM will not be rectified ` +
+      `in production due to performance overhead.` +
+      `\n  You should fix the source of the mismatch.`
+    if (__TEST__) {
+      // during tests, log the full message in one single string for easier
+      // debugging.
+      warn(`${preSegment} ${el.tagName}${postSegment}`)
+    } else {
+      warn(preSegment, el, postSegment)
+    }
     return true
   }
   return false