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 (!(child.flags & DynamicFlag.NON_TEMPLATE)) {
|
||||||
if (prevDynamics.length) {
|
if (prevDynamics.length) {
|
||||||
if (hasStaticTemplate) {
|
if (hasStaticTemplate) {
|
||||||
context.childrenTemplate[index - prevDynamics.length] = `<!>`
|
// each dynamic child gets its own placeholder node.
|
||||||
prevDynamics[0].flags -= DynamicFlag.NON_TEMPLATE
|
// this makes it easier to locate the corresponding node during hydration.
|
||||||
const anchor = (prevDynamics[0].anchor = context.increaseId())
|
for (let i = 0; i < prevDynamics.length; i++) {
|
||||||
registerInsertion(prevDynamics, context, anchor)
|
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 {
|
} else {
|
||||||
registerInsertion(prevDynamics, context, -1 /* prepend */)
|
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(
|
const { container, data } = await testHydration(
|
||||||
`<template>
|
`<template>
|
||||||
<div>
|
<div>
|
||||||
|
@ -333,11 +333,15 @@ describe('Vapor Mode hydration', () => {
|
||||||
Child: `<template>{{ data }}</template>`,
|
Child: `<template>{{ data }}</template>`,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
expect(container.innerHTML).toMatchInlineSnapshot(``)
|
expect(container.innerHTML).toMatchInlineSnapshot(
|
||||||
|
`"<div><span></span><!--[[-->foo<!--]]--><!--[[--> <!--]]--><!--[[--> foo <!--]]--><!--[[--> <!--]]--><!--[[-->foo<!--]]--><span></span></div>"`,
|
||||||
|
)
|
||||||
|
|
||||||
data.value = 'bar'
|
data.value = 'bar'
|
||||||
await nextTick()
|
await nextTick()
|
||||||
expect(container.innerHTML).toMatchInlineSnapshot(``)
|
expect(container.innerHTML).toMatchInlineSnapshot(
|
||||||
|
`"<div><span></span><!--[[-->bar<!--]]--><!--[[--> <!--]]--><!--[[--> bar <!--]]--><!--[[--> <!--]]--><!--[[-->bar<!--]]--><span></span></div>"`,
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
test.todo('if')
|
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 adoptTemplate: (node: Node, template: string) => Node | null
|
||||||
export let locateHydrationNode: () => void
|
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
|
node.nodeType === 8 && (node as Comment).data === data
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -76,16 +76,8 @@ function locateHydrationNodeImpl() {
|
||||||
if (insertionAnchor === 0) {
|
if (insertionAnchor === 0) {
|
||||||
node = child(insertionParent!)
|
node = child(insertionParent!)
|
||||||
} else if (insertionAnchor) {
|
} else if (insertionAnchor) {
|
||||||
// dynamic anchor `<!--[[-->`
|
// for dynamic children, use insertionAnchor as the node
|
||||||
if (isDynamicStart(insertionAnchor)) {
|
node = insertionAnchor
|
||||||
const anchor = (insertionParent!.$lds = insertionParent!.$lds
|
|
||||||
? // continuous dynamic children, the next dynamic start must exist
|
|
||||||
locateNextDynamicStart(insertionParent!.$lds)!
|
|
||||||
: insertionAnchor)
|
|
||||||
node = anchor.nextSibling
|
|
||||||
} else {
|
|
||||||
node = insertionAnchor
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
node = insertionParent ? insertionParent.lastChild : currentHydrationNode
|
node = insertionParent ? insertionParent.lastChild : currentHydrationNode
|
||||||
if (node && isComment(node, ']')) {
|
if (node && isComment(node, ']')) {
|
||||||
|
@ -127,32 +119,3 @@ function locateHydrationNodeImpl() {
|
||||||
resetInsertionState()
|
resetInsertionState()
|
||||||
currentHydrationNode = node
|
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__ */
|
/*! #__NO_SIDE_EFFECTS__ */
|
||||||
|
|
||||||
|
import { isComment, isHydrating } from './hydration'
|
||||||
|
|
||||||
export function createTextNode(value = ''): Text {
|
export function createTextNode(value = ''): Text {
|
||||||
return document.createTextNode(value)
|
return document.createTextNode(value)
|
||||||
}
|
}
|
||||||
|
@ -25,5 +28,20 @@ export function nthChild(node: Node, i: number): Node {
|
||||||
|
|
||||||
/*! #__NO_SIDE_EFFECTS__ */
|
/*! #__NO_SIDE_EFFECTS__ */
|
||||||
export function next(node: Node): Node {
|
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