]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
refactor: move $dp into setInsertionState
authordaiwei <daiwei521@126.com>
Mon, 4 Aug 2025 06:45:24 +0000 (14:45 +0800)
committerdaiwei <daiwei521@126.com>
Mon, 4 Aug 2025 09:36:39 +0000 (17:36 +0800)
packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap
packages/compiler-vapor/src/generate.ts
packages/compiler-vapor/src/generators/operation.ts
packages/compiler-vapor/src/generators/template.ts
packages/compiler-vapor/src/ir/index.ts
packages/compiler-vapor/src/transforms/transformChildren.ts
packages/runtime-vapor/src/dom/hydration.ts
packages/runtime-vapor/src/insertionState.ts

index 693e6593f4004a942243a9a7313b4c370f649304..f1682eaef4e2e214393f358c3606e8d901bc4215 100644 (file)
@@ -218,11 +218,11 @@ const t0 = _template("<div><div></div></div>", 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
 }"
index ff3806611addf37b4f0dc8351306c8260bf22204..337f27799622fc92e8f6c8f8d93bb4bc931382ea 100644 (file)
@@ -39,6 +39,8 @@ export class CodegenContext {
 
   seenInlineHandlerNames: Record<string, number> = Object.create(null)
 
+  seenChildIndexes: Map<number, number> = new Map()
+
   block: BlockIRNode
   withId<T>(
     fn: () => T,
index 13ce5477cc1705908d777c3d75ae18089a82c809..83fcc9a2835e40fd924cba2da7a8ca14627935fc 100644 (file)
@@ -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,
     ),
   ]
 }
index 1fec07f38db4f237c8e9ce622fc83cd786285dd4..30710143328ac3267e578f114829a091290fc7c5 100644 (file)
@@ -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))
     }
index 0e499c040e900138fe3d24716fecc9ab2dadf101..90d063b8b04c3a5f66545b1c6aa54051975692ec 100644 (file)
@@ -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
 }
index 56d3caf5144f5e47cd87876ab185f912571ed54b..5480187ebd911441fbc7e0c6ad17cafff91075fa 100644 (file)
@@ -59,8 +59,7 @@ export const transformChildren: NodeTransform = (node, context) => {
 
 function processDynamicChildren(context: TransformContext<ElementNode>) {
   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<ElementNode>) {
 
     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<ElementNode>) {
             }
           }
         } 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
     }
   }
 }
index 5edf0ec5f4d2acbdd4da438af67f40afeb087278..0186300585a2aa8c429565ca8232b00a32b00f62 100644 (file)
@@ -1,6 +1,7 @@
 import { warn } from '@vue/runtime-dom'
 import {
   insertionAnchor,
+  insertionChildIndex,
   insertionParent,
   resetInsertionState,
   setInsertionState,
@@ -45,7 +46,6 @@ function performHydration<T>(
     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)
     }
   }
 
index f76ceb8b110ba1c8dffde4b4095803c1eec67317..c89884cda8438093d49f6e1be6ff48328b9d6b99 100644 (file)
@@ -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("<div><span></span><span></span></div>", 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
 }