fix(ssr): fix hydration mismatch for disabled teleport at component root (#9399)

close #6152
This commit is contained in:
Carlos Rodrigues 2023-10-21 04:37:52 +01:00 committed by GitHub
parent a8f663867b
commit d8990fc618
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 43 additions and 18 deletions

View File

@ -393,6 +393,28 @@ describe('SSR hydration', () => {
) )
}) })
// #6152
test('Teleport (disabled + as component root)', () => {
const { container } = mountWithHydration(
'<!--[--><div>Parent fragment</div><!--teleport start--><div>Teleport content</div><!--teleport end--><!--]-->',
() => [
h('div', 'Parent fragment'),
h(() =>
h(Teleport, { to: 'body', disabled: true }, [
h('div', 'Teleport content')
])
)
]
)
expect(document.body.innerHTML).toBe('')
expect(container.innerHTML).toBe(
'<!--[--><div>Parent fragment</div><!--teleport start--><div>Teleport content</div><!--teleport end--><!--]-->'
)
expect(
`Hydration completed but contains mismatches.`
).not.toHaveBeenWarned()
})
test('Teleport (as component root)', () => { test('Teleport (as component root)', () => {
const teleportContainer = document.createElement('div') const teleportContainer = document.createElement('div')
teleportContainer.id = 'teleport4' teleportContainer.id = 'teleport4'

View File

@ -227,20 +227,18 @@ export function createHydrationFunctions(
optimized optimized
) )
// component may be async, so in the case of fragments we cannot rely // Locate the next node.
// on component's rendered output to determine the end of the fragment if (isFragmentStart) {
// instead, we do a lookahead to find the end anchor node. // If it's a fragment: since components may be async, we cannot rely
nextNode = isFragmentStart // on component's rendered output to determine the end of the
? locateClosingAsyncAnchor(node) // fragment. Instead, we do a lookahead to find the end anchor node.
: nextSibling(node) nextNode = locateClosingAnchor(node)
} else if (isComment(node) && node.data === 'teleport start') {
// #4293 teleport as component root // #4293 #6152
if ( // If a teleport is at component root, look ahead for teleport end.
nextNode && nextNode = locateClosingAnchor(node, node.data, 'teleport end')
isComment(nextNode) && } else {
nextNode.data === 'teleport end' nextNode = nextSibling(node)
) {
nextNode = nextSibling(nextNode)
} }
// #3787 // #3787
@ -533,7 +531,7 @@ export function createHydrationFunctions(
if (isFragment) { if (isFragment) {
// remove excessive fragment nodes // remove excessive fragment nodes
const end = locateClosingAsyncAnchor(node) const end = locateClosingAnchor(node)
while (true) { while (true) {
const next = nextSibling(node) const next = nextSibling(node)
if (next && next !== end) { if (next && next !== end) {
@ -561,13 +559,18 @@ export function createHydrationFunctions(
return next return next
} }
const locateClosingAsyncAnchor = (node: Node | null): Node | null => { // looks ahead for a start and closing comment node
const locateClosingAnchor = (
node: Node | null,
open = '[',
close = ']'
): Node | null => {
let match = 0 let match = 0
while (node) { while (node) {
node = nextSibling(node) node = nextSibling(node)
if (node && isComment(node)) { if (node && isComment(node)) {
if (node.data === '[') match++ if (node.data === open) match++
if (node.data === ']') { if (node.data === close) {
if (match === 0) { if (match === 0) {
return nextSibling(node) return nextSibling(node)
} else { } else {