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
}"
seenInlineHandlerNames: Record<string, number> = Object.create(null)
+ seenChildIndexes: Map<number, number> = new Map()
+
block: BlockIRNode
withId<T>(
fn: () => T,
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,
),
]
}
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))
}
}
}
- if (child.dynamicChildOffset) {
- pushBlock(`; ${variable}.$dp = ${child.dynamicChildOffset};`)
- }
-
if (id === child.anchor && !child.hasDynamicChild) {
push(...genSelf(child, context))
}
once?: boolean
parent?: number
anchor?: number
+ childIndex?: number
}
export interface IRFor {
onlyChild: boolean
parent?: number
anchor?: number
+ childIndex?: number
}
export interface SetPropIRNode extends BaseIRNode {
dynamic?: SimpleExpressionNode
parent?: number
anchor?: number
+ childIndex?: number
scopeId?: string | null
}
forwarded?: boolean
parent?: number
anchor?: number
+ childIndex?: number
}
export interface GetTextChildIRNode extends BaseIRNode {
children: IRDynamicInfo[]
template?: number
hasDynamicChild?: boolean
- dynamicChildOffset?: number
operation?: OperationNode
needsKey?: boolean
}
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()) {
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++) {
}
}
} 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]),
+ )
}
}
dynamics: IRDynamicInfo[],
context: TransformContext,
anchor?: number,
+ childIndex?: number,
) {
for (const child of dynamics) {
if (child.template != null) {
// block types
child.operation.parent = context.reference()
child.operation.anchor = anchor
+ child.operation.childIndex = childIndex
}
}
}
import { warn } from '@vue/runtime-dom'
import {
insertionAnchor,
+ insertionChildIndex,
insertionParent,
resetInsertionState,
setInsertionState,
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
}
} else {
node = currentHydrationNode
if (insertionParent && (!node || node.parentNode !== insertionParent)) {
- node = __nthChild(insertionParent, insertionParent.$dp || 0)
+ node = __nthChild(insertionParent, insertionChildIndex || 0)
}
}
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
| 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)
}
export function resetInsertionState(): void {
- insertionParent = insertionAnchor = undefined
+ insertionParent = insertionAnchor = insertionChildIndex = undefined
}