perf: cache static children on parent

This commit is contained in:
daiwei 2025-09-08 17:33:01 +08:00
parent 41509dcc61
commit 479ea96e59
4 changed files with 22 additions and 31 deletions

View File

@ -20,8 +20,8 @@ describe('api: template', () => {
test('nthChild', () => {
const t = template('<div><span><b>nested</b></span><p></p></div>')
const root = t()
const span = nthChild(root, 0)
const root = t() as ParentNode
const span = nthChild(root, 0) as ParentNode
const b = nthChild(span, 0)
const p = nthChild(root, 1)
expect(span).toBe(root.firstChild)
@ -31,7 +31,7 @@ describe('api: template', () => {
test('next', () => {
const t = template('<div><span></span><b></b><p></p></div>')
const root = t()
const root = t() as ParentNode
const span = child(root as ParentNode)
const b = next(span)

View File

@ -30,6 +30,7 @@ function performHydration<T>(
// optimize anchor cache lookup
;(Comment.prototype as any).$fe = undefined
;(Node.prototype as any).$idx = undefined
;(Node.prototype as any).$children = undefined
isOptimized = true
}
enableHydrationNodeLookup()

View File

@ -2,8 +2,8 @@
import {
type ChildItem,
type InsertionParent,
getHydrationState,
getTemplateChildren,
} from '../insertionState'
export function createElement(tagName: string): HTMLElement {
@ -46,23 +46,23 @@ const __txt: typeof __child = (node: ParentNode): Node => {
}
/* @__NO_SIDE_EFFECTS__ */
export function _child(node: ParentNode): Node {
const templateChildren = getTemplateChildren(node)
return templateChildren ? templateChildren[0] : node.firstChild!
export function _child(node: InsertionParent): Node {
const children = node.$children
return children ? children[0] : node.firstChild!
}
/**
* Hydration-specific version of `child`.
*/
/* @__NO_SIDE_EFFECTS__ */
export function __child(node: ParentNode & { $lpn?: Node }): Node {
export function __child(node: ParentNode): Node {
return __nthChild(node, 0)!
}
/* @__NO_SIDE_EFFECTS__ */
export function _nthChild(node: Node, i: number): Node {
const templateChildren = getTemplateChildren(node as ParentNode)
return templateChildren ? templateChildren[i] : node.childNodes[i]
export function _nthChild(node: InsertionParent, i: number): Node {
const children = node.$children
return children ? children[i] : node.childNodes[i]
}
/**
@ -92,10 +92,8 @@ export function __nthChild(node: Node, i: number): Node {
/* @__NO_SIDE_EFFECTS__ */
export function _next(node: Node): Node {
const templateChildren = getTemplateChildren(node.parentNode!)
return templateChildren
? templateChildren[(node as ChildItem).$idx + 1]
: node.nextSibling!
const children = (node.parentNode! as InsertionParent).$children
return children ? children[(node as ChildItem).$idx + 1] : node.nextSibling!
}
/**

View File

@ -1,18 +1,16 @@
import { isHydrating } from './dom/hydration'
export interface ChildItem extends ChildNode {
$idx: number
}
export type ChildItem = ChildNode & { $idx: number }
export type InsertionParent = ParentNode & { $children?: ChildItem[] }
type HydrationState = {
logicalChildren: ChildItem[]
prevDynamicCount: number
insertionAnchors: Map<Node, number> | null
appendAnchor: Node | null
}
export let insertionParent: ParentNode | undefined
export let insertionParent: InsertionParent | undefined
export let insertionAnchor: Node | 0 | undefined | null
const templateChildrenCache = new WeakMap<ParentNode, ChildItem[]>()
const hydrationStateCache = new WeakMap<ParentNode, HydrationState>()
/**
@ -87,22 +85,22 @@ function initializeHydrationState(
function cacheTemplateChildren(
anchor: number | Node | null | undefined,
parent: ParentNode,
parent: InsertionParent,
) {
// special handling append anchor value to null
insertionAnchor =
typeof anchor === 'number' && anchor > 0 ? null : (anchor as Node)
if (!templateChildrenCache.has(parent)) {
if (!parent.$children) {
const nodes = parent.childNodes
const len = nodes.length
const children = new Array(len)
const children = new Array(len) as ChildItem[]
for (let i = 0; i < len; i++) {
const node = nodes[i] as ChildItem
node.$idx = i
children[i] = node
}
templateChildrenCache.set(parent, children)
parent.$children = children
}
}
@ -110,12 +108,6 @@ export function resetInsertionState(): void {
insertionParent = insertionAnchor = undefined
}
export function getTemplateChildren(
parent: ParentNode,
): ChildItem[] | undefined {
return templateChildrenCache.get(parent)
}
export function getHydrationState(
parent: ParentNode,
): HydrationState | undefined {