Skip to content

Commit 9f7dced

Browse files
committed
wip: update
1 parent e5399c3 commit 9f7dced

File tree

8 files changed

+124
-69
lines changed

8 files changed

+124
-69
lines changed

packages/runtime-vapor/__tests__/hydration.spec.ts

+65-4
Original file line numberDiff line numberDiff line change
@@ -1963,8 +1963,56 @@ describe('Vapor Mode hydration', () => {
19631963
data,
19641964
)
19651965

1966-
expect(container.innerHTML).toMatchInlineSnapshot(
1967-
`"<div><!--[--><span>foo</span><!--]--><!--${slotAnchorLabel}-->hi</div>"`,
1966+
expect(container.innerHTML).toBe(
1967+
`<div>` +
1968+
`<!--[--><span>foo</span><!--]--><!--${slotAnchorLabel}-->` +
1969+
`hi` +
1970+
`</div>`,
1971+
)
1972+
1973+
data.msg = 'bar'
1974+
await nextTick()
1975+
expect(container.innerHTML).toBe(
1976+
`<div>` +
1977+
`<!--[--><span>foo</span><!--]--><!--${slotAnchorLabel}-->` +
1978+
`bar` +
1979+
`</div>`,
1980+
)
1981+
})
1982+
1983+
test('mixed root slot and text node', async () => {
1984+
const data = reactive({
1985+
text: 'foo',
1986+
msg: 'hi',
1987+
})
1988+
const { container } = await testHydration(
1989+
`<template>
1990+
<components.Child>
1991+
<span>{{data.text}}</span>
1992+
</components.Child>
1993+
</template>`,
1994+
{
1995+
Child: `<template>{{data.text}}<slot/>{{data.msg}}</template>`,
1996+
},
1997+
data,
1998+
)
1999+
2000+
expect(container.innerHTML).toBe(
2001+
`<!--[-->` +
2002+
`foo` +
2003+
`<!--[--><span>foo</span><!--]--><!--${slotAnchorLabel}-->` +
2004+
`hi` +
2005+
`<!--]-->`,
2006+
)
2007+
2008+
data.msg = 'bar'
2009+
await nextTick()
2010+
expect(container.innerHTML).toBe(
2011+
`<!--[-->` +
2012+
`foo` +
2013+
`<!--[--><span>foo</span><!--]--><!--${slotAnchorLabel}-->` +
2014+
`bar` +
2015+
`<!--]-->`,
19682016
)
19692017
})
19702018

@@ -1985,8 +2033,20 @@ describe('Vapor Mode hydration', () => {
19852033
data,
19862034
)
19872035

1988-
expect(container.innerHTML).toMatchInlineSnapshot(
1989-
`"<div><!--[--><span>foo</span><!--]--><!--${slotAnchorLabel}--><div>hi</div></div>"`,
2036+
expect(container.innerHTML).toBe(
2037+
`<div>` +
2038+
`<!--[--><span>foo</span><!--]--><!--${slotAnchorLabel}-->` +
2039+
`<div>hi</div>` +
2040+
`</div>`,
2041+
)
2042+
2043+
data.msg = 'bar'
2044+
await nextTick()
2045+
expect(container.innerHTML).toBe(
2046+
`<div>` +
2047+
`<!--[--><span>foo</span><!--]--><!--${slotAnchorLabel}-->` +
2048+
`<div>bar</div>` +
2049+
`</div>`,
19902050
)
19912051
})
19922052

@@ -2024,6 +2084,7 @@ describe('Vapor Mode hydration', () => {
20242084
`<div>bar</div>` +
20252085
`</div>`,
20262086
)
2087+
20272088
data.msg2 = 'hello'
20282089
await nextTick()
20292090
expect(container.innerHTML).toBe(

packages/runtime-vapor/src/apiCreateDynamicComponent.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@ export function createDynamicComponent(
2222
const _insertionAnchor = insertionAnchor
2323
if (!isHydrating) resetInsertionState()
2424

25-
const frag = new DynamicFragment(DYNAMIC_COMPONENT_ANCHOR_LABEL)
25+
const frag =
26+
isHydrating || __DEV__
27+
? new DynamicFragment(DYNAMIC_COMPONENT_ANCHOR_LABEL)
28+
: new DynamicFragment()
2629
renderEffect(() => {
2730
const value = getter()
2831
frag.update(

packages/runtime-vapor/src/apiCreateFor.ts

+2-5
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,7 @@ import {
1616
isObject,
1717
isString,
1818
} from '@vue/shared'
19-
import {
20-
createComment,
21-
createTextNode,
22-
findVaporFragmentAnchor,
23-
} from './dom/node'
19+
import { createComment, createTextNode } from './dom/node'
2420
import {
2521
type Block,
2622
VaporFragment,
@@ -34,6 +30,7 @@ import { renderEffect } from './renderEffect'
3430
import { VaporVForFlags } from '../../shared/src/vaporFlags'
3531
import {
3632
currentHydrationNode,
33+
findVaporFragmentAnchor,
3734
isHydrating,
3835
locateHydrationNode,
3936
} from './dom/hydration'

packages/runtime-vapor/src/apiCreateIf.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@ export function createIf(
2222
if (once) {
2323
frag = condition() ? b1() : b2 ? b2() : []
2424
} else {
25-
frag = new DynamicFragment(IF_ANCHOR_LABEL)
25+
frag =
26+
isHydrating || __DEV__
27+
? new DynamicFragment(IF_ANCHOR_LABEL)
28+
: new DynamicFragment()
2629
renderEffect(() => (frag as DynamicFragment).update(condition() ? b1 : b2))
2730
}
2831

packages/runtime-vapor/src/block.ts

+4-9
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,11 @@ import {
55
mountComponent,
66
unmountComponent,
77
} from './component'
8-
import {
9-
createComment,
10-
createTextNode,
11-
findVaporFragmentAnchor,
12-
} from './dom/node'
8+
import { createComment, createTextNode } from './dom/node'
139
import { EffectScope, pauseTracking, resetTracking } from '@vue/reactivity'
1410
import {
1511
currentHydrationNode,
12+
findVaporFragmentAnchor,
1613
isComment,
1714
isHydrating,
1815
locateHydrationNode,
@@ -92,13 +89,11 @@ export class DynamicFragment extends VaporFragment {
9289
}
9390

9491
hydrate(label: string): void {
95-
// for v-if="false" the hydrationNode will be a empty comment node
96-
// use it as anchor.
97-
// otherwise, use the next sibling comment node as anchor
92+
// for `v-if="false"` the node will be an empty comment node use it as the anchor.
93+
// otherwise, find next sibling vapor fragment anchor
9894
if (isComment(currentHydrationNode!, '')) {
9995
this.anchor = currentHydrationNode
10096
} else {
101-
// find next sibling dynamic fragment end anchor
10297
const anchor = findVaporFragmentAnchor(currentHydrationNode!, label)!
10398
if (anchor) {
10499
this.anchor = anchor

packages/runtime-vapor/src/componentSlots.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,10 @@ export function createSlot(
124124
fallback,
125125
)
126126
} else {
127-
fragment = new DynamicFragment(SLOT_ANCHOR_LABEL)
127+
fragment =
128+
isHydrating || __DEV__
129+
? new DynamicFragment(SLOT_ANCHOR_LABEL)
130+
: new DynamicFragment()
128131
const isDynamicName = isFunction(name)
129132
const renderSlot = () => {
130133
const slot = getSlot(rawSlots, isFunction(name) ? name() : name)

packages/runtime-vapor/src/dom/hydration.ts

+27-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
enableHydrationNodeLookup,
1212
next,
1313
} from './node'
14-
import { isVaporFragmentEndAnchor } from '@vue/shared'
14+
import { isDynamicAnchor, isVaporFragmentEndAnchor } from '@vue/shared'
1515

1616
export let isHydrating = false
1717
export let currentHydrationNode: Node | null = null
@@ -191,3 +191,29 @@ export function locateEndAnchor(
191191
}
192192
return null
193193
}
194+
195+
export function isNonHydrationNode(node: Node): boolean {
196+
return (
197+
// empty text nodes
198+
isEmptyText(node) ||
199+
// dynamic node anchors (<!--[[-->, <!--]]-->)
200+
isDynamicAnchor(node) ||
201+
// fragment end anchor (`<!--]-->`)
202+
isComment(node, ']') ||
203+
// vapor fragment end anchors
204+
isVaporFragmentEndAnchor(node)
205+
)
206+
}
207+
208+
export function findVaporFragmentAnchor(
209+
node: Node,
210+
anchorLabel: string,
211+
): Comment | null {
212+
let n = node.nextSibling
213+
while (n) {
214+
if (isComment(n, anchorLabel)) return n
215+
n = n.nextSibling
216+
}
217+
218+
return null
219+
}

packages/runtime-vapor/src/dom/node.ts

+14-47
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
import { isComment, isEmptyText, locateEndAnchor } from './hydration'
1+
import { isComment, isNonHydrationNode, locateEndAnchor } from './hydration'
22
import {
33
DYNAMIC_END_ANCHOR_LABEL,
44
DYNAMIC_START_ANCHOR_LABEL,
5-
isDynamicAnchor,
65
isVaporFragmentEndAnchor,
76
} from '@vue/shared'
87

@@ -79,7 +78,19 @@ function _next(node: Node): Node {
7978

8079
/*! #__NO_SIDE_EFFECTS__ */
8180
export function __next(node: Node): Node {
82-
node = handleWrappedNode(node)
81+
// process dynamic node (<!--[[-->...<!--]]-->) as a single node
82+
if (isComment(node, DYNAMIC_START_ANCHOR_LABEL)) {
83+
node = locateEndAnchor(
84+
node,
85+
DYNAMIC_START_ANCHOR_LABEL,
86+
DYNAMIC_END_ANCHOR_LABEL,
87+
)!
88+
}
89+
90+
// process fragment (<!--[-->...<!--]-->) as a single node
91+
else if (isComment(node, '[')) {
92+
node = locateEndAnchor(node)!
93+
}
8394

8495
let n = node.nextSibling!
8596
while (n && isNonHydrationNode(n)) {
@@ -142,47 +153,3 @@ export function disableHydrationNodeLookup(): void {
142153
next.impl = _next
143154
nthChild.impl = _nthChild
144155
}
145-
146-
function isNonHydrationNode(node: Node) {
147-
return (
148-
// empty text nodes, no need to hydrate
149-
isEmptyText(node) ||
150-
// dynamic node anchors (<!--[[-->, <!--]]-->)
151-
isDynamicAnchor(node) ||
152-
// fragment end anchor (`<!--]-->`)
153-
isComment(node, ']') ||
154-
// vapor fragment end anchors
155-
isVaporFragmentEndAnchor(node)
156-
)
157-
}
158-
159-
export function findVaporFragmentAnchor(
160-
node: Node,
161-
anchorLabel: string,
162-
): Comment | null {
163-
let n = node.nextSibling
164-
while (n) {
165-
if (isComment(n, anchorLabel)) return n
166-
n = n.nextSibling
167-
}
168-
169-
return null
170-
}
171-
172-
function handleWrappedNode(node: Node): Node {
173-
// process dynamic node (<!--[[-->...<!--]]-->) as a single one
174-
if (isComment(node, DYNAMIC_START_ANCHOR_LABEL)) {
175-
return locateEndAnchor(
176-
node,
177-
DYNAMIC_START_ANCHOR_LABEL,
178-
DYNAMIC_END_ANCHOR_LABEL,
179-
)!
180-
}
181-
182-
// process fragment (<!--[-->...<!--]-->) as a single one
183-
else if (isComment(node, '[')) {
184-
return locateEndAnchor(node)!
185-
}
186-
187-
return node
188-
}

0 commit comments

Comments
 (0)