wip: refactor

This commit is contained in:
daiwei 2025-04-22 21:20:44 +08:00
parent 9c30fd4db9
commit d8443d3754
5 changed files with 99 additions and 83 deletions

View File

@ -397,8 +397,8 @@ describe('ssr: element', () => {
})
})
describe('dynamic child anchor', () => {
test('with consecutive components', () => {
describe('dynamic anchor', () => {
test('consecutive components', () => {
expect(
getCompiledString(`
<div>
@ -409,14 +409,11 @@ describe('ssr: element', () => {
</div>
`),
).toMatchInlineSnapshot(`
"\`<div><div></div>\`)
_push("<!--[[-->")
"\`<div><div></div><!--[[-->\`)
_push(_ssrRenderComponent(_component_Comp1, null, null, _parent))
_push("<!--]]-->")
_push("<!--[[-->")
_push(\`<!--]]--><!--[[-->\`)
_push(_ssrRenderComponent(_component_Comp2, null, null, _parent))
_push("<!--]]-->")
_push(\`<div></div></div>\`"
_push(\`<!--]]--><div></div></div>\`"
`)
})
})

View File

@ -157,13 +157,29 @@ export function processChildren(
asFragment = false,
disableNestedFragments = false,
disableComment = false,
asDynamic = false,
): void {
if (asDynamic) {
context.pushStringPart(`<!--[[-->`)
}
if (asFragment) {
context.pushStringPart(`<!--[-->`)
}
const { children } = parent
for (let i = 0; i < children.length; i++) {
const child = children[i]
if (shouldProcessAsDynamic(parent, child)) {
processChildren(
{ children: [child] },
context,
asFragment,
disableNestedFragments,
disableComment,
true,
)
continue
}
switch (child.type) {
case NodeTypes.ELEMENT:
switch (child.tagType) {
@ -237,6 +253,9 @@ export function processChildren(
if (asFragment) {
context.pushStringPart(`<!--]-->`)
}
if (asDynamic) {
context.pushStringPart(`<!--]]-->`)
}
}
export function processChildrenAsStatement(
@ -249,3 +268,72 @@ export function processChildrenAsStatement(
processChildren(parent, childContext, asFragment)
return createBlockStatement(childContext.body)
}
const isStaticElement = (c: TemplateChildNode): boolean =>
c.type === NodeTypes.ELEMENT && c.tagType !== ElementTypes.COMPONENT
/**
* Check if a node should be processed as dynamic.
* This is primarily used in Vapor mode hydration to wrap dynamic parts
* with markers (`<!--[[-->` and `<!--]]-->`).
*
* <element>
* <element/> // Static previous sibling
* <Comp/> // Dynamic node (current)
* <Comp/> // Dynamic next sibling
* <element/> // Static next sibling
* </element>
*/
function shouldProcessAsDynamic(
parent: { tag?: string; children: TemplateChildNode[] },
node: TemplateChildNode,
): boolean {
// 1. Must be a dynamic node type
if (isStaticElement(node)) return false
// 2. Must be inside a parent element
if (!parent.tag) return false
const children = parent.children
const len = children.length
const index = children.indexOf(node)
// 3. Check for a static previous sibling
let hasStaticPreviousSibling = false
if (index > 0) {
for (let i = index - 1; i >= 0; i--) {
if (isStaticElement(children[i])) {
hasStaticPreviousSibling = true
break
}
}
}
if (!hasStaticPreviousSibling) return false
// 4. Check for a static next sibling
let hasStaticNextSibling = false
if (index > -1 && index < len - 1) {
for (let i = index + 1; i < len; i++) {
if (isStaticElement(children[i])) {
hasStaticNextSibling = true
break
}
}
}
if (!hasStaticNextSibling) return false
// 5. Check for a consecutive dynamic sibling (immediately before or after)
let hasConsecutiveDynamicNodes = false
if (index > 0 && !isStaticElement(children[index - 1])) {
hasConsecutiveDynamicNodes = true
}
if (
!hasConsecutiveDynamicNodes &&
index < len - 1 &&
!isStaticElement(children[index + 1])
) {
hasConsecutiveDynamicNodes = true
}
// Only process as dynamic if all conditions are met
return hasConsecutiveDynamicNodes
}

View File

@ -255,13 +255,6 @@ export function ssrProcessComponent(
node.ssrCodegenNode.arguments.push(`_scopeId`)
}
// `<!--[[-->` marks the start of the dynamic children
// Only used in Vapor hydration, VDOM hydration
// skips this marker.
const needDynamicAnchor = shouldAddDynamicAnchor(parent, node)
if (needDynamicAnchor) {
context.pushStatement(createCallExpression(`_push`, [`"<!--[[-->"`]))
}
if (typeof component === 'string') {
// static component
context.pushStatement(
@ -272,9 +265,6 @@ export function ssrProcessComponent(
// the codegen node is a `renderVNode` call
context.pushStatement(node.ssrCodegenNode)
}
if (needDynamicAnchor) {
context.pushStatement(createCallExpression(`_push`, [`"<!--]]-->"`]))
}
}
}
@ -394,58 +384,3 @@ function clone(v: any): any {
return v
}
}
function shouldAddDynamicAnchor(
parent: { tag?: string; children: TemplateChildNode[] },
node: TemplateChildNode,
): boolean {
if (!parent.tag) return false
const children = parent.children
const len = children.length
const index = children.indexOf(node)
const isStaticElement = (c: TemplateChildNode): boolean =>
c.type === NodeTypes.ELEMENT && c.tagType !== ElementTypes.COMPONENT
let hasStaticPreviousSibling = false
if (index > 0) {
for (let i = index - 1; i >= 0; i--) {
if (isStaticElement(children[i])) {
hasStaticPreviousSibling = true
break
}
}
}
let hasStaticNextSibling = false
if (hasStaticPreviousSibling && index > -1 && index < len - 1) {
for (let i = index + 1; i < len; i++) {
if (isStaticElement(children[i])) {
hasStaticNextSibling = true
break
}
}
}
let hasConsecutiveDynamicNodes = false
if (index > 0 && index < len - 1) {
if (index > 0 && !isStaticElement(children[index - 1])) {
hasConsecutiveDynamicNodes = true
}
if (
!hasConsecutiveDynamicNodes &&
index < len - 1 &&
!isStaticElement(children[index + 1])
) {
hasConsecutiveDynamicNodes = true
}
}
return (
hasStaticPreviousSibling &&
hasStaticNextSibling &&
hasConsecutiveDynamicNodes
)
}

View File

@ -1843,8 +1843,8 @@ describe('SSR hydration', () => {
}
})
describe('dynamic child anchor', () => {
test('with consecutive components', () => {
describe('dynamic anchor', () => {
test('consecutive components', () => {
const Comp = {
render() {
return createTextVNode('foo')

View File

@ -75,9 +75,9 @@ function locateHydrationNodeImpl() {
// prepend / firstChild
if (insertionAnchor === 0) {
node = child(insertionParent!)
} else if (insertionParent && insertionAnchor) {
// dynamic child anchor `<!--[[-->`
if (insertionAnchor && isDynamicStart(insertionAnchor)) {
} 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)!
@ -87,11 +87,7 @@ function locateHydrationNodeImpl() {
node = insertionAnchor
}
} else {
node = insertionAnchor
? insertionAnchor.previousSibling
: insertionParent
? insertionParent.lastChild
: currentHydrationNode
node = insertionParent ? insertionParent.lastChild : currentHydrationNode
if (node && isComment(node, ']')) {
// fragment backward search
if (node.$fs) {