From 3fc906c3917ba9d0d2453b3b49b716cf8d60dfe1 Mon Sep 17 00:00:00 2001 From: daiwei Date: Mon, 4 Aug 2025 14:45:24 +0800 Subject: [PATCH] refactor: move $dp into setInsertionState --- .../__snapshots__/compile.spec.ts.snap | 4 +-- packages/compiler-vapor/src/generate.ts | 2 ++ .../src/generators/operation.ts | 26 ++++++++++++++----- .../compiler-vapor/src/generators/template.ts | 8 +----- packages/compiler-vapor/src/ir/index.ts | 5 +++- .../src/transforms/transformChildren.ts | 25 ++++++++++++------ packages/runtime-vapor/src/dom/hydration.ts | 4 +-- packages/runtime-vapor/src/insertionState.ts | 20 +++++++------- 8 files changed, 57 insertions(+), 37 deletions(-) diff --git a/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap b/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap index 693e6593f4..f1682eaef4 100644 --- a/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap @@ -218,11 +218,11 @@ const t0 = _template("
", true) export function render(_ctx) { const _component_Comp = _resolveComponent("Comp") - const n3 = t0(); n3.$dp = 1; + const n3 = t0() const n1 = _child(n3) _setInsertionState(n1) const n0 = _createSlot("default", null) - _setInsertionState(n3) + _setInsertionState(n3, null, 1) const n2 = _createComponentWithFallback(_component_Comp) return n3 }" diff --git a/packages/compiler-vapor/src/generate.ts b/packages/compiler-vapor/src/generate.ts index ff3806611a..337f277996 100644 --- a/packages/compiler-vapor/src/generate.ts +++ b/packages/compiler-vapor/src/generate.ts @@ -39,6 +39,8 @@ export class CodegenContext { seenInlineHandlerNames: Record = Object.create(null) + seenChildIndexes: Map = new Map() + block: BlockIRNode withId( fn: () => T, diff --git a/packages/compiler-vapor/src/generators/operation.ts b/packages/compiler-vapor/src/generators/operation.ts index 13ce5477cc..83fcc9a283 100644 --- a/packages/compiler-vapor/src/generators/operation.ts +++ b/packages/compiler-vapor/src/generators/operation.ts @@ -168,16 +168,30 @@ function genInsertionState( operation: InsertionStateTypes, context: CodegenContext, ): CodeFragment[] { + const { seenChildIndexes } = context + let { parent, childIndex, anchor } = operation + const insertionAnchor = + anchor == null + ? undefined + : anchor === -1 // -1 indicates prepend + ? `0` // runtime anchor value for prepend + : `n${anchor}` + + // the index of next block node, used to locate node during hydration + let index: number | undefined + if (anchor == null) { + const existingOffset = seenChildIndexes.get(parent!) + index = existingOffset ? existingOffset + 1 : childIndex + if (index) seenChildIndexes.set(parent!, index) + } + return [ NEWLINE, ...genCall( context.helper('setInsertionState'), - `n${operation.parent}`, - operation.anchor == null - ? undefined - : operation.anchor === -1 // -1 indicates prepend - ? `0` // runtime anchor value for prepend - : `n${operation.anchor}`, + `n${parent}`, + insertionAnchor, + index ? `${index}` : undefined, ), ] } diff --git a/packages/compiler-vapor/src/generators/template.ts b/packages/compiler-vapor/src/generators/template.ts index 1fec07f38d..3071014332 100644 --- a/packages/compiler-vapor/src/generators/template.ts +++ b/packages/compiler-vapor/src/generators/template.ts @@ -24,12 +24,10 @@ export function genSelf( context: CodegenContext, ): CodeFragment[] { const [frag, push] = buildCodeFragment() - const { id, template, operation, dynamicChildOffset, hasDynamicChild } = - dynamic + const { id, template, operation, hasDynamicChild } = dynamic if (id !== undefined && template !== undefined) { push(NEWLINE, `const n${id} = t${template}()`) - if (dynamicChildOffset) push(`; n${id}.$dp = ${dynamicChildOffset};`) push(...genDirectivesForElement(id, context)) } @@ -115,10 +113,6 @@ export function genChildren( } } - if (child.dynamicChildOffset) { - pushBlock(`; ${variable}.$dp = ${child.dynamicChildOffset};`) - } - if (id === child.anchor && !child.hasDynamicChild) { push(...genSelf(child, context)) } diff --git a/packages/compiler-vapor/src/ir/index.ts b/packages/compiler-vapor/src/ir/index.ts index 0e499c040e..90d063b8b0 100644 --- a/packages/compiler-vapor/src/ir/index.ts +++ b/packages/compiler-vapor/src/ir/index.ts @@ -80,6 +80,7 @@ export interface IfIRNode extends BaseIRNode { once?: boolean parent?: number anchor?: number + childIndex?: number } export interface IRFor { @@ -99,6 +100,7 @@ export interface ForIRNode extends BaseIRNode, IRFor { onlyChild: boolean parent?: number anchor?: number + childIndex?: number } export interface SetPropIRNode extends BaseIRNode { @@ -200,6 +202,7 @@ export interface CreateComponentIRNode extends BaseIRNode { dynamic?: SimpleExpressionNode parent?: number anchor?: number + childIndex?: number scopeId?: string | null } @@ -217,6 +220,7 @@ export interface SlotOutletIRNode extends BaseIRNode { forwarded?: boolean parent?: number anchor?: number + childIndex?: number } export interface GetTextChildIRNode extends BaseIRNode { @@ -266,7 +270,6 @@ export interface IRDynamicInfo { children: IRDynamicInfo[] template?: number hasDynamicChild?: boolean - dynamicChildOffset?: number operation?: OperationNode needsKey?: boolean } diff --git a/packages/compiler-vapor/src/transforms/transformChildren.ts b/packages/compiler-vapor/src/transforms/transformChildren.ts index 56d3caf514..5480187ebd 100644 --- a/packages/compiler-vapor/src/transforms/transformChildren.ts +++ b/packages/compiler-vapor/src/transforms/transformChildren.ts @@ -59,8 +59,7 @@ export const transformChildren: NodeTransform = (node, context) => { function processDynamicChildren(context: TransformContext) { let prevDynamics: IRDynamicInfo[] = [] - let staticCount = 0 - let prependCount = 0 + let hasStaticTemplate = false const children = context.dynamic.children for (const [index, child] of children.entries()) { @@ -70,7 +69,7 @@ function processDynamicChildren(context: TransformContext) { if (!(child.flags & DynamicFlag.NON_TEMPLATE)) { if (prevDynamics.length) { - if (staticCount) { + if (hasStaticTemplate) { // 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++) { @@ -89,18 +88,26 @@ function processDynamicChildren(context: TransformContext) { } } } else { - prependCount += prevDynamics.length - registerInsertion(prevDynamics, context, -1 /* prepend */) + registerInsertion( + prevDynamics, + context, + -1 /* prepend */, + children.findIndex(c => c === prevDynamics[0]), + ) } prevDynamics = [] } - staticCount++ + hasStaticTemplate = true } } if (prevDynamics.length) { - registerInsertion(prevDynamics, context) - context.dynamic.dynamicChildOffset = staticCount + prependCount + registerInsertion( + prevDynamics, + context, + undefined, + children.findIndex(c => c === prevDynamics[0]), + ) } } @@ -108,6 +115,7 @@ function registerInsertion( dynamics: IRDynamicInfo[], context: TransformContext, anchor?: number, + childIndex?: number, ) { for (const child of dynamics) { if (child.template != null) { @@ -122,6 +130,7 @@ function registerInsertion( // block types child.operation.parent = context.reference() child.operation.anchor = anchor + child.operation.childIndex = childIndex } } } diff --git a/packages/runtime-vapor/src/dom/hydration.ts b/packages/runtime-vapor/src/dom/hydration.ts index 5edf0ec5f4..0186300585 100644 --- a/packages/runtime-vapor/src/dom/hydration.ts +++ b/packages/runtime-vapor/src/dom/hydration.ts @@ -1,6 +1,7 @@ import { warn } from '@vue/runtime-dom' import { insertionAnchor, + insertionChildIndex, insertionParent, resetInsertionState, setInsertionState, @@ -45,7 +46,6 @@ function performHydration( locateHydrationNode = locateHydrationNodeImpl // optimize anchor cache lookup ;(Comment.prototype as any).$fe = undefined - ;(Node.prototype as any).$dp = undefined ;(Node.prototype as any).$np = undefined isOptimized = true } @@ -129,7 +129,7 @@ function locateHydrationNodeImpl(): void { } else { node = currentHydrationNode if (insertionParent && (!node || node.parentNode !== insertionParent)) { - node = __nthChild(insertionParent, insertionParent.$dp || 0) + node = __nthChild(insertionParent, insertionChildIndex || 0) } } diff --git a/packages/runtime-vapor/src/insertionState.ts b/packages/runtime-vapor/src/insertionState.ts index f76ceb8b11..c89884cda8 100644 --- a/packages/runtime-vapor/src/insertionState.ts +++ b/packages/runtime-vapor/src/insertionState.ts @@ -3,15 +3,6 @@ import { isHydrating } from './dom/hydration' export let insertionParent: | (ParentNode & { - // dynamic node position - hydration only - // indicates the position where dynamic nodes begin within the parent - // during hydration, static nodes before this index are skipped - // - // Example: - // const t0 = _template("
", true) - // const n4 = t0(2) // n4.$dp = 2 - // The first 2 nodes are static, dynamic nodes start from index 2 - $dp?: number // number of prepends - hydration only // consecutive prepends need to skip nodes that were prepended earlier // each prepend increases the value of $prepend @@ -20,14 +11,21 @@ export let insertionParent: | undefined export let insertionAnchor: Node | 0 | undefined +export let insertionChildIndex: number | undefined + /** * This function is called before a block type that requires insertion * (component, slot outlet, if, for) is created. The state is used for actual * insertion on client-side render, and used for node adoption during hydration. */ -export function setInsertionState(parent: ParentNode, anchor?: Node | 0): void { +export function setInsertionState( + parent: ParentNode, + anchor?: Node | 0, + offset?: number, +): void { insertionParent = parent insertionAnchor = anchor + insertionChildIndex = offset if (isHydrating) { collectInsertionParents(parent) @@ -35,5 +33,5 @@ export function setInsertionState(parent: ParentNode, anchor?: Node | 0): void { } export function resetInsertionState(): void { - insertionParent = insertionAnchor = undefined + insertionParent = insertionAnchor = insertionChildIndex = undefined } -- 2.47.2