Просмотр исходного кода

refactor: move $dp into setInsertionState

daiwei 10 месяцев назад
Родитель
Сommit
3fc906c391

+ 2 - 2
packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap

@@ -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
 }"

+ 2 - 0
packages/compiler-vapor/src/generate.ts

@@ -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,

+ 20 - 6
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,
     ),
   ]
 }

+ 1 - 7
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))
     }

+ 4 - 1
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
 }

+ 17 - 8
packages/compiler-vapor/src/transforms/transformChildren.ts

@@ -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
     }
   }
 }

+ 2 - 2
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<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)
     }
   }
 

+ 9 - 11
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("<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
 }