From 8d57f77fa0d8bdf5c7d3620784dc6d4f182ea7c4 Mon Sep 17 00:00:00 2001 From: Evan You Date: Thu, 11 Jan 2024 16:18:00 +0800 Subject: [PATCH] fix(hydration): do not warn against bindings w/ object values --- .../runtime-core/__tests__/hydration.spec.ts | 34 ++++++++++------ packages/runtime-core/src/hydration.ts | 39 ++++++++++++------- 2 files changed, 48 insertions(+), 25 deletions(-) diff --git a/packages/runtime-core/__tests__/hydration.spec.ts b/packages/runtime-core/__tests__/hydration.spec.ts index cd0c61982b8..17d7b8dbcee 100644 --- a/packages/runtime-core/__tests__/hydration.spec.ts +++ b/packages/runtime-core/__tests__/hydration.spec.ts @@ -1080,13 +1080,11 @@ describe('SSR hydration', () => { }) test('force hydrate prop with `.prop` modifier', () => { - const { container } = mountWithHydration( - '', - () => - h('input', { - type: 'checkbox', - '.indeterminate': true, - }), + const { container } = mountWithHydration('', () => + h('input', { + type: 'checkbox', + '.indeterminate': true, + }), ) expect((container.firstChild! as any).indeterminate).toBe(true) }) @@ -1475,6 +1473,16 @@ describe('SSR hydration', () => { mountWithHydration(``, () => h('textarea', { value: 'foo' }), ) @@ -1483,11 +1491,10 @@ describe('SSR hydration', () => { ) expect(`Hydration attribute mismatch`).not.toHaveBeenWarned() - mountWithHydration(`
`, () => h('div', { id: 'foo' })) + mountWithHydration(``, () => + h('textarea', { value: 'bar' }), + ) expect(`Hydration attribute mismatch`).toHaveBeenWarned() - - mountWithHydration(`
`, () => 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(``, () => h('input', { from: {} })) + expect(`Hydration attribute mismatch`).not.toHaveBeenWarned() + }) }) }) diff --git a/packages/runtime-core/src/hydration.ts b/packages/runtime-core/src/hydration.ts index 8f095f6167f..4df4eecece9 100644 --- a/packages/runtime-core/src/hydration.ts +++ b/packages/runtime-core/src/hydration.ts @@ -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