mirror of https://github.com/vuejs/core.git
wip: save
This commit is contained in:
parent
3108d91dfa
commit
1248172216
|
|
@ -264,7 +264,7 @@ describe('Vapor Mode hydration', () => {
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('consecutive component with anchor insertion', async () => {
|
test('consecutive components with anchor insertion', async () => {
|
||||||
const { container, data } = await testHydration(
|
const { container, data } = await testHydration(
|
||||||
`<template>
|
`<template>
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -344,6 +344,111 @@ describe('Vapor Mode hydration', () => {
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('fragment component with anchor insertion', async () => {
|
||||||
|
const { container, data } = await testHydration(
|
||||||
|
`<template>
|
||||||
|
<div>
|
||||||
|
<span/>
|
||||||
|
<components.Child/>
|
||||||
|
<span/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
`,
|
||||||
|
{
|
||||||
|
Child: `<template><div>{{ data }}</div>-{{ data }}</template>`,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
expect(container.innerHTML).toMatchInlineSnapshot(
|
||||||
|
`"<div><span></span><!--[--><div>foo</div>-foo<!--]--><span></span></div>"`,
|
||||||
|
)
|
||||||
|
|
||||||
|
data.value = 'bar'
|
||||||
|
await nextTick()
|
||||||
|
expect(container.innerHTML).toMatchInlineSnapshot(
|
||||||
|
`"<div><span></span><!--[--><div>bar</div>-bar<!--]--><span></span></div>"`,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('consecutive fragment components with anchor insertion', async () => {
|
||||||
|
const { container, data } = await testHydration(
|
||||||
|
`<template>
|
||||||
|
<div>
|
||||||
|
<span/>
|
||||||
|
<components.Child/>
|
||||||
|
<components.Child/>
|
||||||
|
<span/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
`,
|
||||||
|
{
|
||||||
|
Child: `<template><div>{{ data }}</div>-{{ data }}</template>`,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
expect(container.innerHTML).toMatchInlineSnapshot(
|
||||||
|
`"<div><span></span><!--[[--><!--[--><div>foo</div>-foo<!--]--><!--]]--><!--[[--><!--[--><div>foo</div>-foo<!--]--><!--]]--><span></span></div>"`,
|
||||||
|
)
|
||||||
|
|
||||||
|
data.value = 'bar'
|
||||||
|
await nextTick()
|
||||||
|
expect(container.innerHTML).toMatchInlineSnapshot(
|
||||||
|
`"<div><span></span><!--[[--><!--[--><div>bar</div>-bar<!--]--><!--]]--><!--[[--><!--[--><div>bar</div>-bar<!--]--><!--]]--><span></span></div>"`,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('mixed fragment component and element with anchor insertion', async () => {
|
||||||
|
const { container, data } = await testHydration(
|
||||||
|
`<template>
|
||||||
|
<div>
|
||||||
|
<span/>
|
||||||
|
<components.Child/>
|
||||||
|
<span/>
|
||||||
|
<components.Child/>
|
||||||
|
<span/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
`,
|
||||||
|
{
|
||||||
|
Child: `<template><div>{{ data }}</div>-{{ data }}</template>`,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
expect(container.innerHTML).toMatchInlineSnapshot(
|
||||||
|
`"<div><span></span><!--[--><div>foo</div>-foo<!--]--><span></span><!--[--><div>foo</div>-foo<!--]--><span></span></div>"`,
|
||||||
|
)
|
||||||
|
|
||||||
|
data.value = 'bar'
|
||||||
|
await nextTick()
|
||||||
|
expect(container.innerHTML).toMatchInlineSnapshot(
|
||||||
|
`"<div><span></span><!--[--><div>bar</div>-bar<!--]--><span></span><!--[--><div>bar</div>-bar<!--]--><span></span></div>"`,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('mixed fragment component and text with anchor insertion', async () => {
|
||||||
|
const { container, data } = await testHydration(
|
||||||
|
`<template>
|
||||||
|
<div>
|
||||||
|
<span/>
|
||||||
|
<components.Child/>
|
||||||
|
{{ data }}
|
||||||
|
<components.Child/>
|
||||||
|
<span/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
`,
|
||||||
|
{
|
||||||
|
Child: `<template><div>{{ data }}</div>-{{ data }}</template>`,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
expect(container.innerHTML).toMatchInlineSnapshot(
|
||||||
|
`"<div><span></span><!--[[--><!--[--><div>foo</div>-foo<!--]--><!--]]--><!--[[--> <!--]]--><!--[[--> foo <!--]]--><!--[[--> <!--]]--><!--[[--><!--[--><div>foo</div>-foo<!--]--><!--]]--><span></span></div>"`,
|
||||||
|
)
|
||||||
|
|
||||||
|
data.value = 'bar'
|
||||||
|
await nextTick()
|
||||||
|
expect(container.innerHTML).toMatchInlineSnapshot(
|
||||||
|
`"<div><span></span><!--[[--><!--[--><div>bar</div>-bar<!--]--><!--]]--><!--[[--> <!--]]--><!--[[--> bar <!--]]--><!--[[--> <!--]]--><!--[[--><!--[--><div>bar</div>-bar<!--]--><!--]]--><span></span></div>"`,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
test.todo('if')
|
test.todo('if')
|
||||||
|
|
||||||
test.todo('for')
|
test.todo('for')
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ export const isComment = (node: Node, data: string): node is Anchor =>
|
||||||
*/
|
*/
|
||||||
function adoptTemplateImpl(node: Node, template: string): Node | null {
|
function adoptTemplateImpl(node: Node, template: string): Node | null {
|
||||||
if (!(template[0] === '<' && template[1] === '!')) {
|
if (!(template[0] === '<' && template[1] === '!')) {
|
||||||
while (node.nodeType === 8) node = next(node)
|
while (node.nodeType === 8) node = node.nextSibling!
|
||||||
}
|
}
|
||||||
|
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
|
|
@ -119,3 +119,33 @@ function locateHydrationNodeImpl() {
|
||||||
resetInsertionState()
|
resetInsertionState()
|
||||||
currentHydrationNode = node
|
currentHydrationNode = node
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isDynamicAnchor(node: Node): node is Comment {
|
||||||
|
return isComment(node, '[[') || isComment(node, ']]')
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isEmptyText(node: Node): node is Text {
|
||||||
|
return node.nodeType === 3 && !(node as Text).data.trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
export function locateEndAnchor(
|
||||||
|
node: Node | null,
|
||||||
|
open = '[',
|
||||||
|
close = ']',
|
||||||
|
): Node | null {
|
||||||
|
let match = 0
|
||||||
|
while (node) {
|
||||||
|
node = node.nextSibling
|
||||||
|
if (node && node.nodeType === 8) {
|
||||||
|
if ((node as Comment).data === open) match++
|
||||||
|
if ((node as Comment).data === close) {
|
||||||
|
if (match === 0) {
|
||||||
|
return node
|
||||||
|
} else {
|
||||||
|
match--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,12 @@
|
||||||
|
import {
|
||||||
|
isComment,
|
||||||
|
isDynamicAnchor,
|
||||||
|
isEmptyText,
|
||||||
|
isHydrating,
|
||||||
|
locateEndAnchor,
|
||||||
|
} from './hydration'
|
||||||
|
|
||||||
/*! #__NO_SIDE_EFFECTS__ */
|
/*! #__NO_SIDE_EFFECTS__ */
|
||||||
|
|
||||||
import { isComment, isHydrating } from './hydration'
|
|
||||||
|
|
||||||
export function createTextNode(value = ''): Text {
|
export function createTextNode(value = ''): Text {
|
||||||
return document.createTextNode(value)
|
return document.createTextNode(value)
|
||||||
}
|
}
|
||||||
|
|
@ -23,25 +28,28 @@ export function child(node: ParentNode): Node {
|
||||||
|
|
||||||
/*! #__NO_SIDE_EFFECTS__ */
|
/*! #__NO_SIDE_EFFECTS__ */
|
||||||
export function nthChild(node: Node, i: number): Node {
|
export function nthChild(node: Node, i: number): Node {
|
||||||
return node.childNodes[i]
|
if (!isHydrating) return node.childNodes[i]
|
||||||
}
|
|
||||||
|
|
||||||
/*! #__NO_SIDE_EFFECTS__ */
|
let n = node.firstChild!
|
||||||
export function next(node: Node): Node {
|
for (let start = 0; start < i; start++) {
|
||||||
let n = node.nextSibling!
|
n = next(n) as ChildNode
|
||||||
if (isHydrating) {
|
|
||||||
// skip dynamic anchors and empty text nodes
|
|
||||||
while (n && (isDynamicAnchor(n) || isEmptyText(n))) {
|
|
||||||
n = n.nextSibling!
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
function isDynamicAnchor(node: Node): node is Comment {
|
/*! #__NO_SIDE_EFFECTS__ */
|
||||||
return isComment(node, '[[') || isComment(node, ']]')
|
export function next(node: Node): Node {
|
||||||
}
|
if (!isHydrating) return node.nextSibling!
|
||||||
|
|
||||||
function isEmptyText(node: Node): node is Text {
|
// process fragment as a single node
|
||||||
return node.nodeType === 3 && !(node as Text).data.trim()
|
if (node && isComment(node, '[')) {
|
||||||
|
node = locateEndAnchor(node)!
|
||||||
|
}
|
||||||
|
|
||||||
|
let n = node.nextSibling!
|
||||||
|
// skip dynamic anchors and empty text nodes
|
||||||
|
while (n && (isDynamicAnchor(n) || isEmptyText(n))) {
|
||||||
|
n = n.nextSibling!
|
||||||
|
}
|
||||||
|
return n
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue