refactor: skip dynamic anchors and empty text nodes
This commit is contained in:
parent
d8443d3754
commit
3108d91dfa
|
@ -70,10 +70,23 @@ function processDynamicChildren(context: TransformContext<ElementNode>) {
|
|||
if (!(child.flags & DynamicFlag.NON_TEMPLATE)) {
|
||||
if (prevDynamics.length) {
|
||||
if (hasStaticTemplate) {
|
||||
context.childrenTemplate[index - prevDynamics.length] = `<!>`
|
||||
prevDynamics[0].flags -= DynamicFlag.NON_TEMPLATE
|
||||
const anchor = (prevDynamics[0].anchor = context.increaseId())
|
||||
registerInsertion(prevDynamics, context, anchor)
|
||||
// each dynamic child gets its own placeholder node.
|
||||
// this makes it easier to locate the corresponding node during hydration.
|
||||
for (let i = 0; i < prevDynamics.length; i++) {
|
||||
const idx = index - prevDynamics.length + i
|
||||
context.childrenTemplate[idx] = `<!>`
|
||||
const dynamicChild = prevDynamics[i]
|
||||
dynamicChild.flags -= DynamicFlag.NON_TEMPLATE
|
||||
const anchor = (dynamicChild.anchor = context.increaseId())
|
||||
if (
|
||||
dynamicChild.operation &&
|
||||
isBlockOperation(dynamicChild.operation)
|
||||
) {
|
||||
// block types
|
||||
dynamicChild.operation.parent = context.reference()
|
||||
dynamicChild.operation.anchor = anchor
|
||||
}
|
||||
}
|
||||
} else {
|
||||
registerInsertion(prevDynamics, context, -1 /* prepend */)
|
||||
}
|
||||
|
|
|
@ -317,7 +317,7 @@ describe('Vapor Mode hydration', () => {
|
|||
)
|
||||
})
|
||||
|
||||
test.todo('mixed component and text with anchor insertion', async () => {
|
||||
test('mixed component and text with anchor insertion', async () => {
|
||||
const { container, data } = await testHydration(
|
||||
`<template>
|
||||
<div>
|
||||
|
@ -333,11 +333,15 @@ describe('Vapor Mode hydration', () => {
|
|||
Child: `<template>{{ data }}</template>`,
|
||||
},
|
||||
)
|
||||
expect(container.innerHTML).toMatchInlineSnapshot(``)
|
||||
expect(container.innerHTML).toMatchInlineSnapshot(
|
||||
`"<div><span></span><!--[[-->foo<!--]]--><!--[[--> <!--]]--><!--[[--> foo <!--]]--><!--[[--> <!--]]--><!--[[-->foo<!--]]--><span></span></div>"`,
|
||||
)
|
||||
|
||||
data.value = 'bar'
|
||||
await nextTick()
|
||||
expect(container.innerHTML).toMatchInlineSnapshot(``)
|
||||
expect(container.innerHTML).toMatchInlineSnapshot(
|
||||
`"<div><span></span><!--[[-->bar<!--]]--><!--[[--> <!--]]--><!--[[--> bar <!--]]--><!--[[--> <!--]]--><!--[[-->bar<!--]]--><span></span></div>"`,
|
||||
)
|
||||
})
|
||||
|
||||
test.todo('if')
|
||||
|
|
|
@ -37,7 +37,7 @@ export function withHydration(container: ParentNode, fn: () => void): void {
|
|||
export let adoptTemplate: (node: Node, template: string) => Node | null
|
||||
export let locateHydrationNode: () => void
|
||||
|
||||
const isComment = (node: Node, data: string): node is Anchor =>
|
||||
export const isComment = (node: Node, data: string): node is Anchor =>
|
||||
node.nodeType === 8 && (node as Comment).data === data
|
||||
|
||||
/**
|
||||
|
@ -76,16 +76,8 @@ function locateHydrationNodeImpl() {
|
|||
if (insertionAnchor === 0) {
|
||||
node = child(insertionParent!)
|
||||
} else if (insertionAnchor) {
|
||||
// dynamic anchor `<!--[[-->`
|
||||
if (isDynamicStart(insertionAnchor)) {
|
||||
const anchor = (insertionParent!.$lds = insertionParent!.$lds
|
||||
? // continuous dynamic children, the next dynamic start must exist
|
||||
locateNextDynamicStart(insertionParent!.$lds)!
|
||||
: insertionAnchor)
|
||||
node = anchor.nextSibling
|
||||
} else {
|
||||
// for dynamic children, use insertionAnchor as the node
|
||||
node = insertionAnchor
|
||||
}
|
||||
} else {
|
||||
node = insertionParent ? insertionParent.lastChild : currentHydrationNode
|
||||
if (node && isComment(node, ']')) {
|
||||
|
@ -127,32 +119,3 @@ function locateHydrationNodeImpl() {
|
|||
resetInsertionState()
|
||||
currentHydrationNode = node
|
||||
}
|
||||
|
||||
function isDynamicStart(node: Node): node is Anchor {
|
||||
return isComment(node, '[[')
|
||||
}
|
||||
|
||||
function locateNextDynamicStart(anchor: Anchor): Anchor | undefined {
|
||||
let cur: Node | null = anchor
|
||||
let end = null
|
||||
let depth = 0
|
||||
while (cur) {
|
||||
cur = cur.nextSibling
|
||||
if (cur) {
|
||||
if (isComment(cur, '[[')) {
|
||||
depth++
|
||||
} else if (isComment(cur, ']]')) {
|
||||
if (!depth) {
|
||||
end = cur
|
||||
break
|
||||
} else {
|
||||
depth--
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (end) {
|
||||
return end!.nextSibling as Anchor
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
/*! #__NO_SIDE_EFFECTS__ */
|
||||
|
||||
import { isComment, isHydrating } from './hydration'
|
||||
|
||||
export function createTextNode(value = ''): Text {
|
||||
return document.createTextNode(value)
|
||||
}
|
||||
|
@ -25,5 +28,20 @@ export function nthChild(node: Node, i: number): Node {
|
|||
|
||||
/*! #__NO_SIDE_EFFECTS__ */
|
||||
export function next(node: Node): Node {
|
||||
return node.nextSibling!
|
||||
let n = node.nextSibling!
|
||||
if (isHydrating) {
|
||||
// skip dynamic anchors and empty text nodes
|
||||
while (n && (isDynamicAnchor(n) || isEmptyText(n))) {
|
||||
n = n.nextSibling!
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
function isDynamicAnchor(node: Node): node is Comment {
|
||||
return isComment(node, '[[') || isComment(node, ']]')
|
||||
}
|
||||
|
||||
function isEmptyText(node: Node): node is Text {
|
||||
return node.nodeType === 3 && !(node as Text).data.trim()
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue