fix(hydration): fix mismatch of leading newline in <textarea> and <pre>

close #11873
close #11874
This commit is contained in:
Evan You 2024-09-13 21:24:40 +08:00
parent 3c4bf76276
commit a5f3c2eb4d
No known key found for this signature in database
GPG Key ID: 00E9AB7A6704CE0A
2 changed files with 27 additions and 2 deletions

View 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()

View 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'
)
}