Skip to content

Commit

Permalink
fix(hydration): do not warn against bindings w/ object values
Browse files Browse the repository at this point in the history
  • Loading branch information
yyx990803 committed Jan 11, 2024
1 parent 8d656ce commit dcc68ef
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 25 deletions.
34 changes: 23 additions & 11 deletions packages/runtime-core/__tests__/hydration.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
})
Expand Down Expand Up @@ -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' }),
)
Expand All @@ -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', () => {
Expand All @@ -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()
})
})
})
39 changes: 25 additions & 14 deletions packages/runtime-core/src/hydration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
isBooleanAttr,
isKnownHtmlAttr,
isKnownSvgAttr,
isObject,
isOn,
isReservedProp,
isString,
Expand Down Expand Up @@ -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`
Expand All @@ -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
Expand Down

0 comments on commit dcc68ef

Please sign in to comment.