mirror of https://github.com/vuejs/core.git
wip: handle mixed prepend and insertionAnchor
This commit is contained in:
parent
6606cbdb4b
commit
a9b911e254
|
@ -69,7 +69,7 @@ export function genChildren(
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
const elementIndex = Number(index) + offset
|
const elementIndex = index + offset
|
||||||
// p for "placeholder" variables that are meant for possible reuse by
|
// p for "placeholder" variables that are meant for possible reuse by
|
||||||
// other access paths
|
// other access paths
|
||||||
const variable = id === undefined ? `p${context.block.tempId++}` : `n${id}`
|
const variable = id === undefined ? `p${context.block.tempId++}` : `n${id}`
|
||||||
|
@ -82,15 +82,24 @@ export function genChildren(
|
||||||
pushBlock(...genCall(helper('nthChild'), from, String(elementIndex)))
|
pushBlock(...genCall(helper('nthChild'), from, String(elementIndex)))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// offset is used to determine the child during hydration.
|
// child index is used to find the child during hydration.
|
||||||
// if offset is not 0, we need to specify the offset to skip the dynamic
|
// if offset is not 0, we need to specify the offset to skip the dynamic
|
||||||
// children and get the correct child.
|
// children and get the correct child.
|
||||||
let childOffset = offset === 0 ? undefined : `${Math.abs(offset)}`
|
const asAnchor = children.some(child => child.anchor === id)
|
||||||
|
let childIndex =
|
||||||
|
offset === 0
|
||||||
|
? undefined
|
||||||
|
: // if the current node is used as insertionAnchor, subtract 1 here
|
||||||
|
// this ensures that insertionAnchor points to the current node itself
|
||||||
|
// rather than its next sibling, since insertionAnchor is used as the
|
||||||
|
// hydration node
|
||||||
|
`${asAnchor ? index - 1 : index}`
|
||||||
|
|
||||||
if (elementIndex === 0) {
|
if (elementIndex === 0) {
|
||||||
pushBlock(...genCall(helper('child'), from, childOffset))
|
pushBlock(...genCall(helper('child'), from, childIndex))
|
||||||
} else {
|
} else {
|
||||||
// check if there's a node that we can reuse from
|
// check if there's a node that we can reuse from
|
||||||
let init = genCall(helper('child'), from, childOffset)
|
let init = genCall(helper('child'), from, childIndex)
|
||||||
if (elementIndex === 1) {
|
if (elementIndex === 1) {
|
||||||
init = genCall(helper('next'), init)
|
init = genCall(helper('next'), init)
|
||||||
} else if (elementIndex > 1) {
|
} else if (elementIndex > 1) {
|
||||||
|
|
|
@ -60,6 +60,7 @@ export const transformChildren: NodeTransform = (node, context) => {
|
||||||
function processDynamicChildren(context: TransformContext<ElementNode>) {
|
function processDynamicChildren(context: TransformContext<ElementNode>) {
|
||||||
let prevDynamics: IRDynamicInfo[] = []
|
let prevDynamics: IRDynamicInfo[] = []
|
||||||
let staticCount = 0
|
let staticCount = 0
|
||||||
|
let prependCount = 0
|
||||||
const children = context.dynamic.children
|
const children = context.dynamic.children
|
||||||
|
|
||||||
for (const [index, child] of children.entries()) {
|
for (const [index, child] of children.entries()) {
|
||||||
|
@ -88,6 +89,7 @@ function processDynamicChildren(context: TransformContext<ElementNode>) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
prependCount += prevDynamics.length
|
||||||
registerInsertion(prevDynamics, context, -1 /* prepend */)
|
registerInsertion(prevDynamics, context, -1 /* prepend */)
|
||||||
}
|
}
|
||||||
prevDynamics = []
|
prevDynamics = []
|
||||||
|
@ -97,8 +99,8 @@ function processDynamicChildren(context: TransformContext<ElementNode>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prevDynamics.length) {
|
if (prevDynamics.length) {
|
||||||
registerInsertion(prevDynamics, context, undefined)
|
registerInsertion(prevDynamics, context)
|
||||||
context.dynamic.dynamicChildOffset = staticCount
|
context.dynamic.dynamicChildOffset = staticCount + prependCount
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1285,6 +1285,65 @@ describe('Vapor Mode hydration', () => {
|
||||||
expect(container.innerHTML).toBe(`<div>foo</div><!--if--><!--if-->`)
|
expect(container.innerHTML).toBe(`<div>foo</div><!--if--><!--if-->`)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('mixed prepend and insertion anchor', async () => {
|
||||||
|
const data = reactive({
|
||||||
|
show: true,
|
||||||
|
foo: 'foo',
|
||||||
|
bar: 'bar',
|
||||||
|
qux: 'qux',
|
||||||
|
})
|
||||||
|
const { container } = await testHydration(
|
||||||
|
`<template>
|
||||||
|
<components.Child/>
|
||||||
|
</template>`,
|
||||||
|
{
|
||||||
|
Child: `<template>
|
||||||
|
<span v-if="data.show">
|
||||||
|
<span v-if="data.show">{{data.foo}}</span>
|
||||||
|
<span v-if="data.show">{{data.bar}}</span>
|
||||||
|
<span>baz</span>
|
||||||
|
<span v-if="data.show">{{data.qux}}</span>
|
||||||
|
<span>quux</span>
|
||||||
|
</span>
|
||||||
|
</template>`,
|
||||||
|
},
|
||||||
|
data,
|
||||||
|
)
|
||||||
|
expect(container.innerHTML).toBe(
|
||||||
|
`<span>` +
|
||||||
|
`<span>foo</span><!--if-->` +
|
||||||
|
`<span>bar</span><!--if-->` +
|
||||||
|
`<span>baz</span>` +
|
||||||
|
`<span>qux</span><!--if-->` +
|
||||||
|
`<span>quux</span>` +
|
||||||
|
`</span><!--if-->`,
|
||||||
|
)
|
||||||
|
|
||||||
|
data.qux = 'qux1'
|
||||||
|
await nextTick()
|
||||||
|
expect(container.innerHTML).toBe(
|
||||||
|
`<span>` +
|
||||||
|
`<span>foo</span><!--if-->` +
|
||||||
|
`<span>bar</span><!--if-->` +
|
||||||
|
`<span>baz</span>` +
|
||||||
|
`<span>qux1</span><!--if-->` +
|
||||||
|
`<span>quux</span>` +
|
||||||
|
`</span><!--if-->`,
|
||||||
|
)
|
||||||
|
|
||||||
|
data.foo = 'foo1'
|
||||||
|
await nextTick()
|
||||||
|
expect(container.innerHTML).toBe(
|
||||||
|
`<span>` +
|
||||||
|
`<span>foo1</span><!--if-->` +
|
||||||
|
`<span>bar</span><!--if-->` +
|
||||||
|
`<span>baz</span>` +
|
||||||
|
`<span>qux1</span><!--if-->` +
|
||||||
|
`<span>quux</span>` +
|
||||||
|
`</span><!--if-->`,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
test('v-if/else-if/else chain on component - switch branches', async () => {
|
test('v-if/else-if/else chain on component - switch branches', async () => {
|
||||||
const data = ref('a')
|
const data = ref('a')
|
||||||
const { container } = await testHydration(
|
const { container } = await testHydration(
|
||||||
|
|
|
@ -10,7 +10,7 @@ import {
|
||||||
resetInsertionState,
|
resetInsertionState,
|
||||||
} from './insertionState'
|
} from './insertionState'
|
||||||
import { DYNAMIC_COMPONENT_ANCHOR_LABEL } from '@vue/shared'
|
import { DYNAMIC_COMPONENT_ANCHOR_LABEL } from '@vue/shared'
|
||||||
import { isHydrating } from './dom/hydration'
|
import { advanceHydrationNode, isHydrating } from './dom/hydration'
|
||||||
import { DynamicFragment, type VaporFragment } from './fragment'
|
import { DynamicFragment, type VaporFragment } from './fragment'
|
||||||
|
|
||||||
export function createDynamicComponent(
|
export function createDynamicComponent(
|
||||||
|
@ -52,5 +52,8 @@ export function createDynamicComponent(
|
||||||
if (!isHydrating && _insertionParent) {
|
if (!isHydrating && _insertionParent) {
|
||||||
insert(frag, _insertionParent, _insertionAnchor)
|
insert(frag, _insertionParent, _insertionAnchor)
|
||||||
}
|
}
|
||||||
|
if (isHydrating && _insertionAnchor !== undefined) {
|
||||||
|
advanceHydrationNode(_insertionParent!)
|
||||||
|
}
|
||||||
return frag
|
return frag
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ import type { DynamicSlot } from './componentSlots'
|
||||||
import { renderEffect } from './renderEffect'
|
import { renderEffect } from './renderEffect'
|
||||||
import { VaporVForFlags } from '../../shared/src/vaporFlags'
|
import { VaporVForFlags } from '../../shared/src/vaporFlags'
|
||||||
import {
|
import {
|
||||||
|
advanceHydrationNode,
|
||||||
currentHydrationNode,
|
currentHydrationNode,
|
||||||
isHydrating,
|
isHydrating,
|
||||||
locateHydrationNode,
|
locateHydrationNode,
|
||||||
|
@ -468,6 +469,9 @@ export const createFor = (
|
||||||
if (!isHydrating && _insertionParent) {
|
if (!isHydrating && _insertionParent) {
|
||||||
insert(frag, _insertionParent, _insertionAnchor)
|
insert(frag, _insertionParent, _insertionAnchor)
|
||||||
}
|
}
|
||||||
|
if (isHydrating && _insertionAnchor !== undefined) {
|
||||||
|
advanceHydrationNode(_insertionParent!)
|
||||||
|
}
|
||||||
|
|
||||||
return frag
|
return frag
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { IF_ANCHOR_LABEL } from '@vue/shared'
|
import { IF_ANCHOR_LABEL } from '@vue/shared'
|
||||||
import { type Block, type BlockFn, insert } from './block'
|
import { type Block, type BlockFn, insert } from './block'
|
||||||
import { isHydrating } from './dom/hydration'
|
import { advanceHydrationNode, isHydrating } from './dom/hydration'
|
||||||
import {
|
import {
|
||||||
insertionAnchor,
|
insertionAnchor,
|
||||||
insertionParent,
|
insertionParent,
|
||||||
|
@ -78,5 +78,12 @@ export function createIf(
|
||||||
insert(frag, _insertionParent, _insertionAnchor)
|
insert(frag, _insertionParent, _insertionAnchor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if _insertionAnchor is defined, insertionParent contains a static node
|
||||||
|
// that should be skipped during hydration.
|
||||||
|
// Advance to the next sibling node to bypass this static node.
|
||||||
|
if (isHydrating && _insertionAnchor !== undefined) {
|
||||||
|
advanceHydrationNode(_insertionParent!)
|
||||||
|
}
|
||||||
|
|
||||||
return frag
|
return frag
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,6 +67,7 @@ import { hmrReload, hmrRerender } from './hmr'
|
||||||
import { createElement } from './dom/node'
|
import { createElement } from './dom/node'
|
||||||
import {
|
import {
|
||||||
adoptTemplate,
|
adoptTemplate,
|
||||||
|
advanceHydrationNode,
|
||||||
currentHydrationNode,
|
currentHydrationNode,
|
||||||
isHydrating,
|
isHydrating,
|
||||||
locateHydrationNode,
|
locateHydrationNode,
|
||||||
|
@ -182,6 +183,9 @@ export function createComponent(
|
||||||
if (_insertionParent) {
|
if (_insertionParent) {
|
||||||
insert(frag, _insertionParent, _insertionAnchor)
|
insert(frag, _insertionParent, _insertionAnchor)
|
||||||
}
|
}
|
||||||
|
if (isHydrating && _insertionAnchor !== undefined) {
|
||||||
|
advanceHydrationNode(_insertionParent!)
|
||||||
|
}
|
||||||
return frag
|
return frag
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -194,6 +198,10 @@ export function createComponent(
|
||||||
frag.hydrate()
|
frag.hydrate()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isHydrating && _insertionAnchor !== undefined) {
|
||||||
|
advanceHydrationNode(_insertionParent!)
|
||||||
|
}
|
||||||
|
|
||||||
return frag as any
|
return frag as any
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -587,6 +595,10 @@ export function createComponentWithFallback(
|
||||||
insert(el, _insertionParent, _insertionAnchor)
|
insert(el, _insertionParent, _insertionAnchor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isHydrating && _insertionAnchor !== undefined) {
|
||||||
|
advanceHydrationNode(_insertionParent!)
|
||||||
|
}
|
||||||
|
|
||||||
return el
|
return el
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ import {
|
||||||
insertionParent,
|
insertionParent,
|
||||||
resetInsertionState,
|
resetInsertionState,
|
||||||
} from './insertionState'
|
} from './insertionState'
|
||||||
import { isHydrating } from './dom/hydration'
|
import { advanceHydrationNode, isHydrating } from './dom/hydration'
|
||||||
import { DynamicFragment } from './fragment'
|
import { DynamicFragment } from './fragment'
|
||||||
|
|
||||||
export type RawSlots = Record<string, VaporSlot> & {
|
export type RawSlots = Record<string, VaporSlot> & {
|
||||||
|
@ -175,6 +175,10 @@ export function createSlot(
|
||||||
insert(fragment, _insertionParent, _insertionAnchor)
|
insert(fragment, _insertionParent, _insertionAnchor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isHydrating && _insertionAnchor !== undefined) {
|
||||||
|
advanceHydrationNode(_insertionParent!)
|
||||||
|
}
|
||||||
|
|
||||||
return fragment
|
return fragment
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,6 @@ import {
|
||||||
import {
|
import {
|
||||||
__next,
|
__next,
|
||||||
__nthChild,
|
__nthChild,
|
||||||
_nthChild,
|
|
||||||
disableHydrationNodeLookup,
|
disableHydrationNodeLookup,
|
||||||
enableHydrationNodeLookup,
|
enableHydrationNodeLookup,
|
||||||
} from './node'
|
} from './node'
|
||||||
|
@ -137,7 +136,7 @@ function locateHydrationNodeImpl(isFragment?: boolean): void {
|
||||||
if (insertionParent && (!node || node.parentNode !== insertionParent)) {
|
if (insertionParent && (!node || node.parentNode !== insertionParent)) {
|
||||||
node =
|
node =
|
||||||
childToHydrateMap.get(insertionParent) ||
|
childToHydrateMap.get(insertionParent) ||
|
||||||
_nthChild(insertionParent, insertionParent.$dp || 0)
|
__nthChild(insertionParent, insertionParent.$dp || 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// locate slot fragment start anchor
|
// locate slot fragment start anchor
|
||||||
|
|
Loading…
Reference in New Issue