mirror of https://github.com/vuejs/core.git
wip: hydration for slots
This commit is contained in:
parent
2f002649d1
commit
700f49ee96
|
@ -246,7 +246,7 @@ describe('ssr: components', () => {
|
||||||
_ssrRenderList(list, (i) => {
|
_ssrRenderList(list, (i) => {
|
||||||
_push(\`<span\${_scopeId}></span>\`)
|
_push(\`<span\${_scopeId}></span>\`)
|
||||||
})
|
})
|
||||||
_push(\`<!--]--></div>\`)
|
_push(\`<!--]--><!--for--></div>\`)
|
||||||
_push(\`<!--if-->\`)
|
_push(\`<!--if-->\`)
|
||||||
} else {
|
} else {
|
||||||
_push(\`<!---->\`)
|
_push(\`<!---->\`)
|
||||||
|
@ -270,7 +270,7 @@ describe('ssr: components', () => {
|
||||||
_ssrRenderList(_ctx.list, (i) => {
|
_ssrRenderList(_ctx.list, (i) => {
|
||||||
_push(\`<span\${_scopeId}></span>\`)
|
_push(\`<span\${_scopeId}></span>\`)
|
||||||
})
|
})
|
||||||
_push(\`<!--]--></div>\`)
|
_push(\`<!--]--><!--for--></div>\`)
|
||||||
_push(\`<!--if-->\`)
|
_push(\`<!--if-->\`)
|
||||||
} else {
|
} else {
|
||||||
_push(\`<!---->\`)
|
_push(\`<!---->\`)
|
||||||
|
|
|
@ -10,7 +10,7 @@ describe('ssr: v-for', () => {
|
||||||
_ssrRenderList(_ctx.list, (i) => {
|
_ssrRenderList(_ctx.list, (i) => {
|
||||||
_push(\`<div></div>\`)
|
_push(\`<div></div>\`)
|
||||||
})
|
})
|
||||||
_push(\`<!--]-->\`)
|
_push(\`<!--]--><!--for-->\`)
|
||||||
}"
|
}"
|
||||||
`)
|
`)
|
||||||
})
|
})
|
||||||
|
@ -25,7 +25,7 @@ describe('ssr: v-for', () => {
|
||||||
_ssrRenderList(_ctx.list, (i) => {
|
_ssrRenderList(_ctx.list, (i) => {
|
||||||
_push(\`<div>foo<span>bar</span></div>\`)
|
_push(\`<div>foo<span>bar</span></div>\`)
|
||||||
})
|
})
|
||||||
_push(\`<!--]-->\`)
|
_push(\`<!--]--><!--for-->\`)
|
||||||
}"
|
}"
|
||||||
`)
|
`)
|
||||||
})
|
})
|
||||||
|
@ -51,9 +51,9 @@ describe('ssr: v-for', () => {
|
||||||
_ssrInterpolate(j)
|
_ssrInterpolate(j)
|
||||||
}</div>\`)
|
}</div>\`)
|
||||||
})
|
})
|
||||||
_push(\`<!--]--></div>\`)
|
_push(\`<!--]--><!--for--></div>\`)
|
||||||
})
|
})
|
||||||
_push(\`<!--]-->\`)
|
_push(\`<!--]--><!--for-->\`)
|
||||||
}"
|
}"
|
||||||
`)
|
`)
|
||||||
})
|
})
|
||||||
|
@ -68,7 +68,7 @@ describe('ssr: v-for', () => {
|
||||||
_ssrRenderList(_ctx.list, (i) => {
|
_ssrRenderList(_ctx.list, (i) => {
|
||||||
_push(\`<!--[-->\${_ssrInterpolate(i)}<!--]-->\`)
|
_push(\`<!--[-->\${_ssrInterpolate(i)}<!--]-->\`)
|
||||||
})
|
})
|
||||||
_push(\`<!--]-->\`)
|
_push(\`<!--]--><!--for-->\`)
|
||||||
}"
|
}"
|
||||||
`)
|
`)
|
||||||
})
|
})
|
||||||
|
@ -85,7 +85,7 @@ describe('ssr: v-for', () => {
|
||||||
_ssrRenderList(_ctx.list, (i) => {
|
_ssrRenderList(_ctx.list, (i) => {
|
||||||
_push(\`<span>\${_ssrInterpolate(i)}</span>\`)
|
_push(\`<span>\${_ssrInterpolate(i)}</span>\`)
|
||||||
})
|
})
|
||||||
_push(\`<!--]-->\`)
|
_push(\`<!--]--><!--for-->\`)
|
||||||
}"
|
}"
|
||||||
`)
|
`)
|
||||||
})
|
})
|
||||||
|
@ -107,7 +107,7 @@ describe('ssr: v-for', () => {
|
||||||
_ssrInterpolate(i + 1)
|
_ssrInterpolate(i + 1)
|
||||||
}</span><!--]-->\`)
|
}</span><!--]-->\`)
|
||||||
})
|
})
|
||||||
_push(\`<!--]-->\`)
|
_push(\`<!--]--><!--for-->\`)
|
||||||
}"
|
}"
|
||||||
`)
|
`)
|
||||||
})
|
})
|
||||||
|
@ -127,7 +127,7 @@ describe('ssr: v-for', () => {
|
||||||
_ssrRenderList(_ctx.list, ({ foo }, index) => {
|
_ssrRenderList(_ctx.list, ({ foo }, index) => {
|
||||||
_push(\`<div>\${_ssrInterpolate(foo + _ctx.bar + index)}</div>\`)
|
_push(\`<div>\${_ssrInterpolate(foo + _ctx.bar + index)}</div>\`)
|
||||||
})
|
})
|
||||||
_push(\`<!--]-->\`)
|
_push(\`<!--]--><!--for-->\`)
|
||||||
}"
|
}"
|
||||||
`)
|
`)
|
||||||
})
|
})
|
||||||
|
|
|
@ -147,7 +147,7 @@ describe('ssr: v-if', () => {
|
||||||
_ssrRenderList(_ctx.list, (i) => {
|
_ssrRenderList(_ctx.list, (i) => {
|
||||||
_push(\`<div></div>\`)
|
_push(\`<div></div>\`)
|
||||||
})
|
})
|
||||||
_push(\`<!--]-->\`)
|
_push(\`<!--]--><!--for-->\`)
|
||||||
_push(\`<!--if-->\`)
|
_push(\`<!--if-->\`)
|
||||||
} else {
|
} else {
|
||||||
_push(\`<!---->\`)
|
_push(\`<!---->\`)
|
||||||
|
|
|
@ -70,7 +70,7 @@ describe('ssr: v-model', () => {
|
||||||
: _ssrLooseEqual(_ctx.model, i))) ? " selected" : ""
|
: _ssrLooseEqual(_ctx.model, i))) ? " selected" : ""
|
||||||
}></option>\`)
|
}></option>\`)
|
||||||
})
|
})
|
||||||
_push(\`<!--]--></select></div>\`)
|
_push(\`<!--]--><!--for--></select></div>\`)
|
||||||
}"
|
}"
|
||||||
`)
|
`)
|
||||||
|
|
||||||
|
|
|
@ -381,6 +381,7 @@ function processChildrenDynamicInfo(
|
||||||
* <Comp/> // Dynamic node -> should be wrapped
|
* <Comp/> // Dynamic node -> should be wrapped
|
||||||
* <Comp/> // Dynamic node -> should NOT be wrapped
|
* <Comp/> // Dynamic node -> should NOT be wrapped
|
||||||
* <element/> // Static node
|
* <element/> // Static node
|
||||||
|
* </element>
|
||||||
*/
|
*/
|
||||||
function shouldProcessChildAsDynamic(
|
function shouldProcessChildAsDynamic(
|
||||||
parent: { tag?: string; children: TemplateChildNode[] },
|
parent: { tag?: string; children: TemplateChildNode[] },
|
||||||
|
|
|
@ -49,8 +49,7 @@ export function ssrProcessFor(
|
||||||
)
|
)
|
||||||
if (!disableNestedFragments) {
|
if (!disableNestedFragments) {
|
||||||
context.pushStringPart(`<!--]-->`)
|
context.pushStringPart(`<!--]-->`)
|
||||||
} else {
|
|
||||||
// add anchor for non-fragment v-for
|
|
||||||
context.pushStringPart(`<!--${FOR_ANCHOR_LABEL}-->`)
|
|
||||||
}
|
}
|
||||||
|
// v-for anchor for vapor hydration
|
||||||
|
context.pushStringPart(`<!--${FOR_ANCHOR_LABEL}-->`)
|
||||||
}
|
}
|
||||||
|
|
|
@ -125,7 +125,7 @@ export function createHydrationFunctions(
|
||||||
let n = next(node)
|
let n = next(node)
|
||||||
// skip if:
|
// skip if:
|
||||||
// - dynamic anchors (`<!--[[-->`, `<!--][-->`)
|
// - dynamic anchors (`<!--[[-->`, `<!--][-->`)
|
||||||
// - dynamic fragment end anchors (e.g. `<!--if-->`, `<!--for-->`)
|
// - vapor fragment end anchors (e.g. `<!--if-->`, `<!--for-->`)
|
||||||
if (n && (isDynamicAnchor(n) || isVaporFragmentEndAnchor(n))) {
|
if (n && (isDynamicAnchor(n) || isVaporFragmentEndAnchor(n))) {
|
||||||
n = next(n)
|
n = next(n)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,12 @@ import { compileScript, parse } from '@vue/compiler-sfc'
|
||||||
import * as runtimeVapor from '../src'
|
import * as runtimeVapor from '../src'
|
||||||
import * as runtimeDom from '@vue/runtime-dom'
|
import * as runtimeDom from '@vue/runtime-dom'
|
||||||
import * as VueServerRenderer from '@vue/server-renderer'
|
import * as VueServerRenderer from '@vue/server-renderer'
|
||||||
import { DYNAMIC_COMPONENT_ANCHOR_LABEL, IF_ANCHOR_LABEL } from '@vue/shared'
|
import {
|
||||||
|
DYNAMIC_COMPONENT_ANCHOR_LABEL,
|
||||||
|
FOR_ANCHOR_LABEL,
|
||||||
|
IF_ANCHOR_LABEL,
|
||||||
|
SLOT_ANCHOR_LABEL,
|
||||||
|
} from '@vue/shared'
|
||||||
|
|
||||||
const Vue = { ...runtimeDom, ...runtimeVapor }
|
const Vue = { ...runtimeDom, ...runtimeVapor }
|
||||||
|
|
||||||
|
@ -1438,6 +1443,9 @@ describe('Vapor Mode hydration', () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('for', () => {
|
describe('for', () => {
|
||||||
|
const forAnchorLabel = FOR_ANCHOR_LABEL
|
||||||
|
const slotAnchorLabel = SLOT_ANCHOR_LABEL
|
||||||
|
|
||||||
test('basic v-for', async () => {
|
test('basic v-for', async () => {
|
||||||
const { container, data } = await testHydration(
|
const { container, data } = await testHydration(
|
||||||
`<template>
|
`<template>
|
||||||
|
@ -1454,7 +1462,7 @@ describe('Vapor Mode hydration', () => {
|
||||||
`<span>a</span>` +
|
`<span>a</span>` +
|
||||||
`<span>b</span>` +
|
`<span>b</span>` +
|
||||||
`<span>c</span>` +
|
`<span>c</span>` +
|
||||||
`<!--]-->` +
|
`<!--]--><!--${forAnchorLabel}-->` +
|
||||||
`</div>`,
|
`</div>`,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1466,8 +1474,9 @@ describe('Vapor Mode hydration', () => {
|
||||||
`<span>a</span>` +
|
`<span>a</span>` +
|
||||||
`<span>b</span>` +
|
`<span>b</span>` +
|
||||||
`<span>c</span>` +
|
`<span>c</span>` +
|
||||||
`<span>d</span>` +
|
|
||||||
`<!--]-->` +
|
`<!--]-->` +
|
||||||
|
`<span>d</span>` +
|
||||||
|
`<!--${forAnchorLabel}-->` +
|
||||||
`</div>`,
|
`</div>`,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -1483,13 +1492,23 @@ describe('Vapor Mode hydration', () => {
|
||||||
ref(['a', 'b', 'c']),
|
ref(['a', 'b', 'c']),
|
||||||
)
|
)
|
||||||
expect(container.innerHTML).toBe(
|
expect(container.innerHTML).toBe(
|
||||||
`<div><!--[--><span>a</span><span>b</span><span>c</span><!--]--></div>`,
|
`<div>` +
|
||||||
|
`<!--[-->` +
|
||||||
|
`<span>a</span><span>b</span><span>c</span>` +
|
||||||
|
`<!--]--><!--${forAnchorLabel}-->` +
|
||||||
|
`</div>`,
|
||||||
)
|
)
|
||||||
|
|
||||||
data.value.push('d')
|
data.value.push('d')
|
||||||
await nextTick()
|
await nextTick()
|
||||||
expect(container.innerHTML).toBe(
|
expect(container.innerHTML).toBe(
|
||||||
`<div><!--[--><span>a</span><span>b</span><span>c</span><span>d</span><!--]--></div>`,
|
`<div>` +
|
||||||
|
`<!--[-->` +
|
||||||
|
`<span>a</span><span>b</span><span>c</span>` +
|
||||||
|
`<!--]-->` +
|
||||||
|
`<span>d</span>` +
|
||||||
|
`<!--${forAnchorLabel}-->` +
|
||||||
|
`</div>`,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -1512,7 +1531,7 @@ describe('Vapor Mode hydration', () => {
|
||||||
`<span>a</span>` +
|
`<span>a</span>` +
|
||||||
`<span>b</span>` +
|
`<span>b</span>` +
|
||||||
`<span>c</span>` +
|
`<span>c</span>` +
|
||||||
`<!--]-->` +
|
`<!--]--><!--${forAnchorLabel}-->` +
|
||||||
`<span></span>` +
|
`<span></span>` +
|
||||||
`</div>`,
|
`</div>`,
|
||||||
)
|
)
|
||||||
|
@ -1526,8 +1545,9 @@ describe('Vapor Mode hydration', () => {
|
||||||
`<span>a</span>` +
|
`<span>a</span>` +
|
||||||
`<span>b</span>` +
|
`<span>b</span>` +
|
||||||
`<span>c</span>` +
|
`<span>c</span>` +
|
||||||
`<span>d</span>` +
|
|
||||||
`<!--]-->` +
|
`<!--]-->` +
|
||||||
|
`<span>d</span>` +
|
||||||
|
`<!--${forAnchorLabel}-->` +
|
||||||
`<span></span>` +
|
`<span></span>` +
|
||||||
`</div>`,
|
`</div>`,
|
||||||
)
|
)
|
||||||
|
@ -1540,8 +1560,9 @@ describe('Vapor Mode hydration', () => {
|
||||||
`<!--[-->` +
|
`<!--[-->` +
|
||||||
`<span>b</span>` +
|
`<span>b</span>` +
|
||||||
`<span>c</span>` +
|
`<span>c</span>` +
|
||||||
`<span>d</span>` +
|
|
||||||
`<!--]-->` +
|
`<!--]-->` +
|
||||||
|
`<span>d</span>` +
|
||||||
|
`<!--${forAnchorLabel}-->` +
|
||||||
`<span></span>` +
|
`<span></span>` +
|
||||||
`</div>`,
|
`</div>`,
|
||||||
)
|
)
|
||||||
|
@ -1567,12 +1588,12 @@ describe('Vapor Mode hydration', () => {
|
||||||
`<span>a</span>` +
|
`<span>a</span>` +
|
||||||
`<span>b</span>` +
|
`<span>b</span>` +
|
||||||
`<span>c</span>` +
|
`<span>c</span>` +
|
||||||
`<!--]-->` +
|
`<!--]--><!--${forAnchorLabel}-->` +
|
||||||
`<!--[-->` +
|
`<!--[-->` +
|
||||||
`<span>a</span>` +
|
`<span>a</span>` +
|
||||||
`<span>b</span>` +
|
`<span>b</span>` +
|
||||||
`<span>c</span>` +
|
`<span>c</span>` +
|
||||||
`<!--]-->` +
|
`<!--]--><!--${forAnchorLabel}-->` +
|
||||||
`<span></span>` +
|
`<span></span>` +
|
||||||
`</div>`,
|
`</div>`,
|
||||||
)
|
)
|
||||||
|
@ -1586,14 +1607,16 @@ describe('Vapor Mode hydration', () => {
|
||||||
`<span>a</span>` +
|
`<span>a</span>` +
|
||||||
`<span>b</span>` +
|
`<span>b</span>` +
|
||||||
`<span>c</span>` +
|
`<span>c</span>` +
|
||||||
`<span>d</span>` +
|
|
||||||
`<!--]-->` +
|
`<!--]-->` +
|
||||||
|
`<span>d</span>` +
|
||||||
|
`<!--${forAnchorLabel}-->` +
|
||||||
`<!--[-->` +
|
`<!--[-->` +
|
||||||
`<span>a</span>` +
|
`<span>a</span>` +
|
||||||
`<span>b</span>` +
|
`<span>b</span>` +
|
||||||
`<span>c</span>` +
|
`<span>c</span>` +
|
||||||
`<span>d</span>` +
|
|
||||||
`<!--]-->` +
|
`<!--]-->` +
|
||||||
|
`<span>d</span>` +
|
||||||
|
`<!--${forAnchorLabel}-->` +
|
||||||
`<span></span>` +
|
`<span></span>` +
|
||||||
`</div>`,
|
`</div>`,
|
||||||
)
|
)
|
||||||
|
@ -1605,12 +1628,14 @@ describe('Vapor Mode hydration', () => {
|
||||||
`<span></span>` +
|
`<span></span>` +
|
||||||
`<!--[-->` +
|
`<!--[-->` +
|
||||||
`<span>c</span>` +
|
`<span>c</span>` +
|
||||||
`<span>d</span>` +
|
|
||||||
`<!--]-->` +
|
`<!--]-->` +
|
||||||
|
`<span>d</span>` +
|
||||||
|
`<!--${forAnchorLabel}-->` +
|
||||||
`<!--[-->` +
|
`<!--[-->` +
|
||||||
`<span>c</span>` +
|
`<span>c</span>` +
|
||||||
`<span>d</span>` +
|
|
||||||
`<!--]-->` +
|
`<!--]-->` +
|
||||||
|
`<span>d</span>` +
|
||||||
|
`<!--${forAnchorLabel}-->` +
|
||||||
`<span></span>` +
|
`<span></span>` +
|
||||||
`</div>`,
|
`</div>`,
|
||||||
)
|
)
|
||||||
|
@ -1635,7 +1660,7 @@ describe('Vapor Mode hydration', () => {
|
||||||
`<div>comp</div>` +
|
`<div>comp</div>` +
|
||||||
`<div>comp</div>` +
|
`<div>comp</div>` +
|
||||||
`<div>comp</div>` +
|
`<div>comp</div>` +
|
||||||
`<!--]-->` +
|
`<!--]--><!--${forAnchorLabel}-->` +
|
||||||
`</div>`,
|
`</div>`,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1647,8 +1672,9 @@ describe('Vapor Mode hydration', () => {
|
||||||
`<div>comp</div>` +
|
`<div>comp</div>` +
|
||||||
`<div>comp</div>` +
|
`<div>comp</div>` +
|
||||||
`<div>comp</div>` +
|
`<div>comp</div>` +
|
||||||
`<div>comp</div>` +
|
|
||||||
`<!--]-->` +
|
`<!--]-->` +
|
||||||
|
`<div>comp</div>` +
|
||||||
|
`<!--${forAnchorLabel}-->` +
|
||||||
`</div>`,
|
`</div>`,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -1670,10 +1696,10 @@ describe('Vapor Mode hydration', () => {
|
||||||
expect(container.innerHTML).toBe(
|
expect(container.innerHTML).toBe(
|
||||||
`<div>` +
|
`<div>` +
|
||||||
`<!--[-->` +
|
`<!--[-->` +
|
||||||
`<!--[--><span>a</span><!--]--><!--slot-->` +
|
`<!--[--><span>a</span><!--]--><!--${slotAnchorLabel}-->` +
|
||||||
`<!--[--><span>b</span><!--]--><!--slot-->` +
|
`<!--[--><span>b</span><!--]--><!--${slotAnchorLabel}-->` +
|
||||||
`<!--[--><span>c</span><!--]--><!--slot-->` +
|
`<!--[--><span>c</span><!--]--><!--${slotAnchorLabel}-->` +
|
||||||
`<!--]-->` +
|
`<!--]--><!--${forAnchorLabel}-->` +
|
||||||
`</div>`,
|
`</div>`,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1682,11 +1708,12 @@ describe('Vapor Mode hydration', () => {
|
||||||
expect(container.innerHTML).toBe(
|
expect(container.innerHTML).toBe(
|
||||||
`<div>` +
|
`<div>` +
|
||||||
`<!--[-->` +
|
`<!--[-->` +
|
||||||
`<!--[--><span>a</span><!--]--><!--slot-->` +
|
`<!--[--><span>a</span><!--]--><!--${slotAnchorLabel}-->` +
|
||||||
`<!--[--><span>b</span><!--]--><!--slot-->` +
|
`<!--[--><span>b</span><!--]--><!--${slotAnchorLabel}-->` +
|
||||||
`<!--[--><span>c</span><!--]--><!--slot-->` +
|
`<!--[--><span>c</span><!--]--><!--${slotAnchorLabel}-->` +
|
||||||
`<span>d</span><!--slot-->` +
|
|
||||||
`<!--]-->` +
|
`<!--]-->` +
|
||||||
|
`<span>d</span><!--${slotAnchorLabel}-->` +
|
||||||
|
`<!--${forAnchorLabel}-->` +
|
||||||
`</div>`,
|
`</div>`,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -1709,7 +1736,7 @@ describe('Vapor Mode hydration', () => {
|
||||||
`<!--[--><div>foo</div>-bar-<!--]-->` +
|
`<!--[--><div>foo</div>-bar-<!--]-->` +
|
||||||
`<!--[--><div>foo</div>-bar-<!--]-->` +
|
`<!--[--><div>foo</div>-bar-<!--]-->` +
|
||||||
`<!--[--><div>foo</div>-bar-<!--]-->` +
|
`<!--[--><div>foo</div>-bar-<!--]-->` +
|
||||||
`<!--]-->` +
|
`<!--]--><!--${forAnchorLabel}-->` +
|
||||||
`</div>`,
|
`</div>`,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1721,18 +1748,17 @@ describe('Vapor Mode hydration', () => {
|
||||||
`<!--[--><div>foo</div>-bar-<!--]-->` +
|
`<!--[--><div>foo</div>-bar-<!--]-->` +
|
||||||
`<!--[--><div>foo</div>-bar-<!--]-->` +
|
`<!--[--><div>foo</div>-bar-<!--]-->` +
|
||||||
`<!--[--><div>foo</div>-bar-<!--]-->` +
|
`<!--[--><div>foo</div>-bar-<!--]-->` +
|
||||||
`<div>foo</div>-bar-` +
|
|
||||||
`<!--]-->` +
|
`<!--]-->` +
|
||||||
|
`<div>foo</div>-bar-` +
|
||||||
|
`<!--${forAnchorLabel}-->` +
|
||||||
`</div>`,
|
`</div>`,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
// TODO wait for vapor TransitionGroup support
|
|
||||||
// v-for inside TransitionGroup does not render as a fragment
|
|
||||||
test.todo('v-for in TransitionGroup', async () => {})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('slots', () => {
|
describe('slots', () => {
|
||||||
|
const slotAnchorLabel = SLOT_ANCHOR_LABEL
|
||||||
|
const forAnchorLabel = FOR_ANCHOR_LABEL
|
||||||
test('basic slot', async () => {
|
test('basic slot', async () => {
|
||||||
const { data, container } = await testHydration(
|
const { data, container } = await testHydration(
|
||||||
`<template>
|
`<template>
|
||||||
|
@ -1745,13 +1771,13 @@ describe('Vapor Mode hydration', () => {
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
expect(container.innerHTML).toBe(
|
expect(container.innerHTML).toBe(
|
||||||
`<!--[--><span>foo</span><!--]--><!--slot-->`,
|
`<!--[--><span>foo</span><!--]--><!--${slotAnchorLabel}-->`,
|
||||||
)
|
)
|
||||||
|
|
||||||
data.value = 'bar'
|
data.value = 'bar'
|
||||||
await nextTick()
|
await nextTick()
|
||||||
expect(container.innerHTML).toBe(
|
expect(container.innerHTML).toBe(
|
||||||
`<!--[--><span>bar</span><!--]--><!--slot-->`,
|
`<!--[--><span>bar</span><!--]--><!--${slotAnchorLabel}-->`,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -1769,13 +1795,13 @@ describe('Vapor Mode hydration', () => {
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
expect(container.innerHTML).toBe(
|
expect(container.innerHTML).toBe(
|
||||||
`<!--[--><span>foo</span><!--]--><!--slot-->`,
|
`<!--[--><span>foo</span><!--]--><!--${slotAnchorLabel}-->`,
|
||||||
)
|
)
|
||||||
|
|
||||||
data.value = 'bar'
|
data.value = 'bar'
|
||||||
await nextTick()
|
await nextTick()
|
||||||
expect(container.innerHTML).toBe(
|
expect(container.innerHTML).toBe(
|
||||||
`<!--[--><span>bar</span><!--]--><!--slot-->`,
|
`<!--[--><span>bar</span><!--]--><!--${slotAnchorLabel}-->`,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -1793,12 +1819,14 @@ describe('Vapor Mode hydration', () => {
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
expect(container.innerHTML).toBe(
|
expect(container.innerHTML).toBe(
|
||||||
`<!--[--><span>foo</span><!--]--><!--slot-->`,
|
`<!--[--><span>foo</span><!--]--><!--${slotAnchorLabel}-->`,
|
||||||
)
|
)
|
||||||
|
|
||||||
data.value = false
|
data.value = false
|
||||||
await nextTick()
|
await nextTick()
|
||||||
expect(container.innerHTML).toBe(`<!--[--><!--]--><!--slot-->`)
|
expect(container.innerHTML).toBe(
|
||||||
|
`<!--[--><!--]--><!--${slotAnchorLabel}-->`,
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('named slot with v-if and v-for', async () => {
|
test('named slot with v-if and v-for', async () => {
|
||||||
|
@ -1821,15 +1849,15 @@ describe('Vapor Mode hydration', () => {
|
||||||
)
|
)
|
||||||
expect(container.innerHTML).toBe(
|
expect(container.innerHTML).toBe(
|
||||||
`<!--[-->` +
|
`<!--[-->` +
|
||||||
`<!--[--><span>a</span><span>b</span><span>c</span><!--]-->` +
|
`<!--[--><span>a</span><span>b</span><span>c</span><!--]--><!--${forAnchorLabel}-->` +
|
||||||
`<!--]-->` +
|
`<!--]-->` +
|
||||||
`<!--slot-->`,
|
`<!--${slotAnchorLabel}-->`,
|
||||||
)
|
)
|
||||||
|
|
||||||
data.show = false
|
data.show = false
|
||||||
await nextTick()
|
await nextTick()
|
||||||
expect(container.innerHTML).toBe(
|
expect(container.innerHTML).toBe(
|
||||||
`<!--[--><!--[--><!--]--><!--]--><!--slot-->`,
|
`<!--[--><!--[--><!--]--><!--]--><!--${slotAnchorLabel}-->`,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -1852,7 +1880,7 @@ describe('Vapor Mode hydration', () => {
|
||||||
`<span>foo</span>` +
|
`<span>foo</span>` +
|
||||||
`<span></span>` +
|
`<span></span>` +
|
||||||
`<!--]-->` +
|
`<!--]-->` +
|
||||||
`<!--slot-->`,
|
`<!--${slotAnchorLabel}-->`,
|
||||||
)
|
)
|
||||||
|
|
||||||
data.value = 'bar'
|
data.value = 'bar'
|
||||||
|
@ -1863,7 +1891,7 @@ describe('Vapor Mode hydration', () => {
|
||||||
`<span>bar</span>` +
|
`<span>bar</span>` +
|
||||||
`<span></span>` +
|
`<span></span>` +
|
||||||
`<!--]-->` +
|
`<!--]-->` +
|
||||||
`<!--slot-->`,
|
`<!--${slotAnchorLabel}-->`,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -1896,7 +1924,7 @@ describe('Vapor Mode hydration', () => {
|
||||||
`<span>foo</span>` +
|
`<span>foo</span>` +
|
||||||
`<span></span>` +
|
`<span></span>` +
|
||||||
`<!--]-->` +
|
`<!--]-->` +
|
||||||
`<!--slot-->` +
|
`<!--${slotAnchorLabel}-->` +
|
||||||
`<div></div>` +
|
`<div></div>` +
|
||||||
`<!--]-->`,
|
`<!--]-->`,
|
||||||
)
|
)
|
||||||
|
@ -1912,14 +1940,13 @@ describe('Vapor Mode hydration', () => {
|
||||||
`<span>bar</span>` +
|
`<span>bar</span>` +
|
||||||
`<span></span>` +
|
`<span></span>` +
|
||||||
`<!--]-->` +
|
`<!--]-->` +
|
||||||
`<!--slot-->` +
|
`<!--${slotAnchorLabel}-->` +
|
||||||
`<div></div>` +
|
`<div></div>` +
|
||||||
`<!--]-->`,
|
`<!--]-->`,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
// problem is next child is incorrect after slot
|
test('mixed slot and text node', async () => {
|
||||||
test.todo('mixed slot and text node', async () => {
|
|
||||||
const data = reactive({
|
const data = reactive({
|
||||||
text: 'foo',
|
text: 'foo',
|
||||||
msg: 'hi',
|
msg: 'hi',
|
||||||
|
@ -1937,11 +1964,11 @@ describe('Vapor Mode hydration', () => {
|
||||||
)
|
)
|
||||||
|
|
||||||
expect(container.innerHTML).toMatchInlineSnapshot(
|
expect(container.innerHTML).toMatchInlineSnapshot(
|
||||||
`"<div><!--[--><span>foo</span><!--]--><!--slot-->hi</div>"`,
|
`"<div><!--[--><span>foo</span><!--]--><!--${slotAnchorLabel}-->hi</div>"`,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
test.todo('mixed slot and element', async () => {
|
test('mixed slot and element', async () => {
|
||||||
const data = reactive({
|
const data = reactive({
|
||||||
text: 'foo',
|
text: 'foo',
|
||||||
msg: 'hi',
|
msg: 'hi',
|
||||||
|
@ -1959,14 +1986,272 @@ describe('Vapor Mode hydration', () => {
|
||||||
)
|
)
|
||||||
|
|
||||||
expect(container.innerHTML).toMatchInlineSnapshot(
|
expect(container.innerHTML).toMatchInlineSnapshot(
|
||||||
`"<div><!--hi--><span>foo</span><!--]--><!--slot--><div>hi</div></div>"`,
|
`"<div><!--[--><span>foo</span><!--]--><!--${slotAnchorLabel}--><div>hi</div></div>"`,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
// mixed slot and component
|
test('mixed slot and component', async () => {
|
||||||
// mixed slot and fragment component
|
const data = reactive({
|
||||||
// mixed slot and v-if
|
msg1: 'foo',
|
||||||
// mixed slot and v-for
|
msg2: 'bar',
|
||||||
|
})
|
||||||
|
const { container } = await testHydration(
|
||||||
|
`<template>
|
||||||
|
<components.Child>
|
||||||
|
<span>{{data.msg1}}</span>
|
||||||
|
</components.Child>
|
||||||
|
</template>`,
|
||||||
|
{
|
||||||
|
Child: `
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<components.Child2/>
|
||||||
|
<slot/>
|
||||||
|
<components.Child2/>
|
||||||
|
</div>
|
||||||
|
</template>`,
|
||||||
|
Child2: `
|
||||||
|
<template>
|
||||||
|
<div>{{data.msg2}}</div>
|
||||||
|
</template>`,
|
||||||
|
},
|
||||||
|
data,
|
||||||
|
)
|
||||||
|
expect(container.innerHTML).toBe(
|
||||||
|
`<div>` +
|
||||||
|
`<div>bar</div>` +
|
||||||
|
`<!--[--><span>foo</span><!--]--><!--${slotAnchorLabel}-->` +
|
||||||
|
`<div>bar</div>` +
|
||||||
|
`</div>`,
|
||||||
|
)
|
||||||
|
data.msg2 = 'hello'
|
||||||
|
await nextTick()
|
||||||
|
expect(container.innerHTML).toBe(
|
||||||
|
`<div>` +
|
||||||
|
`<div>hello</div>` +
|
||||||
|
`<!--[--><span>foo</span><!--]--><!--${slotAnchorLabel}-->` +
|
||||||
|
`<div>hello</div>` +
|
||||||
|
`</div>`,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('mixed slot and fragment component', async () => {
|
||||||
|
const data = reactive({
|
||||||
|
msg1: 'foo',
|
||||||
|
msg2: 'bar',
|
||||||
|
})
|
||||||
|
const { container } = await testHydration(
|
||||||
|
`<template>
|
||||||
|
<components.Child>
|
||||||
|
<span>{{data.msg1}}</span>
|
||||||
|
</components.Child>
|
||||||
|
</template>`,
|
||||||
|
{
|
||||||
|
Child: `
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<components.Child2/>
|
||||||
|
<slot/>
|
||||||
|
<components.Child2/>
|
||||||
|
</div>
|
||||||
|
</template>`,
|
||||||
|
Child2: `
|
||||||
|
<template>
|
||||||
|
<div>{{data.msg1}}</div> {{data.msg2}}
|
||||||
|
</template>`,
|
||||||
|
},
|
||||||
|
data,
|
||||||
|
)
|
||||||
|
expect(container.innerHTML).toBe(
|
||||||
|
`<div>` +
|
||||||
|
`<!--[--><div>foo</div> bar<!--]-->` +
|
||||||
|
`<!--[--><span>foo</span><!--]--><!--${slotAnchorLabel}-->` +
|
||||||
|
`<!--[--><div>foo</div> bar<!--]-->` +
|
||||||
|
`</div>`,
|
||||||
|
)
|
||||||
|
|
||||||
|
data.msg1 = 'hello'
|
||||||
|
data.msg2 = 'vapor'
|
||||||
|
await nextTick()
|
||||||
|
expect(container.innerHTML).toBe(
|
||||||
|
`<div>` +
|
||||||
|
`<!--[--><div>hello</div> vapor<!--]-->` +
|
||||||
|
`<!--[--><span>hello</span><!--]--><!--${slotAnchorLabel}-->` +
|
||||||
|
`<!--[--><div>hello</div> vapor<!--]-->` +
|
||||||
|
`</div>`,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('mixed slot and v-if', async () => {
|
||||||
|
const data = reactive({
|
||||||
|
show: true,
|
||||||
|
msg: 'foo',
|
||||||
|
})
|
||||||
|
const { container } = await testHydration(
|
||||||
|
`<template>
|
||||||
|
<components.Child>
|
||||||
|
<span>{{data.msg}}</span>
|
||||||
|
</components.Child>
|
||||||
|
</template>`,
|
||||||
|
{
|
||||||
|
Child: `
|
||||||
|
<template>
|
||||||
|
<div v-if="data.show">{{data.msg}}</div>
|
||||||
|
<slot/>
|
||||||
|
<div v-if="data.show">{{data.msg}}</div>
|
||||||
|
</template>`,
|
||||||
|
},
|
||||||
|
data,
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(container.innerHTML).toBe(
|
||||||
|
`<!--[-->` +
|
||||||
|
`<div>foo</div><!--if-->` +
|
||||||
|
`<!--[--><span>foo</span><!--]--><!--${slotAnchorLabel}-->` +
|
||||||
|
`<div>foo</div><!--if-->` +
|
||||||
|
`<!--]-->`,
|
||||||
|
)
|
||||||
|
|
||||||
|
data.show = false
|
||||||
|
await nextTick()
|
||||||
|
expect(container.innerHTML).toBe(
|
||||||
|
`<!--[-->` +
|
||||||
|
`<!--if-->` +
|
||||||
|
`<!--[--><span>foo</span><!--]--><!--${slotAnchorLabel}-->` +
|
||||||
|
`<!--if-->` +
|
||||||
|
`<!--]-->`,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('mixed slot and v-for', async () => {
|
||||||
|
const data = reactive({
|
||||||
|
items: ['a', 'b', 'c'],
|
||||||
|
msg: 'foo',
|
||||||
|
})
|
||||||
|
const { container } = await testHydration(
|
||||||
|
`<template>
|
||||||
|
<components.Child>
|
||||||
|
<span>{{data.msg}}</span>
|
||||||
|
</components.Child>
|
||||||
|
</template>`,
|
||||||
|
{
|
||||||
|
Child: `
|
||||||
|
<template>
|
||||||
|
<div v-for="item in data.items" :key="item">{{item}}</div>
|
||||||
|
<slot/>
|
||||||
|
<div v-for="item in data.items" :key="item">{{item}}</div>
|
||||||
|
</template>`,
|
||||||
|
},
|
||||||
|
data,
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(container.innerHTML).toBe(
|
||||||
|
`<!--[-->` +
|
||||||
|
`<!--[--><div>a</div><div>b</div><div>c</div><!--]--><!--${forAnchorLabel}-->` +
|
||||||
|
`<!--[--><span>foo</span><!--]--><!--${slotAnchorLabel}-->` +
|
||||||
|
`<!--[--><div>a</div><div>b</div><div>c</div><!--]--><!--${forAnchorLabel}-->` +
|
||||||
|
`<!--]-->`,
|
||||||
|
)
|
||||||
|
|
||||||
|
data.items.push('d')
|
||||||
|
await nextTick()
|
||||||
|
expect(container.innerHTML).toBe(
|
||||||
|
`<!--[-->` +
|
||||||
|
`<!--[--><div>a</div><div>b</div><div>c</div><!--]--><div>d</div><!--${forAnchorLabel}-->` +
|
||||||
|
`<!--[--><span>foo</span><!--]--><!--${slotAnchorLabel}-->` +
|
||||||
|
`<!--[--><div>a</div><div>b</div><div>c</div><!--]--><div>d</div><!--${forAnchorLabel}-->` +
|
||||||
|
`<!--]-->`,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('consecutive slots', async () => {
|
||||||
|
const data = reactive({
|
||||||
|
msg1: 'foo',
|
||||||
|
msg2: 'bar',
|
||||||
|
})
|
||||||
|
|
||||||
|
const { container } = await testHydration(
|
||||||
|
`<template>
|
||||||
|
<components.Child>
|
||||||
|
<span>{{data.msg1}}</span>
|
||||||
|
<template #bar>
|
||||||
|
<span>{{data.msg2}}</span>
|
||||||
|
</template>
|
||||||
|
</components.Child>
|
||||||
|
</template>`,
|
||||||
|
{
|
||||||
|
Child: `<template><slot/><slot name="bar"/></template>`,
|
||||||
|
},
|
||||||
|
data,
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(container.innerHTML).toBe(
|
||||||
|
`<!--[-->` +
|
||||||
|
`<!--[--><span>foo</span><!--]--><!--${slotAnchorLabel}-->` +
|
||||||
|
`<!--[--><span>bar</span><!--]--><!--${slotAnchorLabel}-->` +
|
||||||
|
`<!--]-->`,
|
||||||
|
)
|
||||||
|
|
||||||
|
data.msg1 = 'hello'
|
||||||
|
data.msg2 = 'vapor'
|
||||||
|
await nextTick()
|
||||||
|
expect(container.innerHTML).toBe(
|
||||||
|
`<!--[-->` +
|
||||||
|
`<!--[--><span>hello</span><!--]--><!--${slotAnchorLabel}-->` +
|
||||||
|
`<!--[--><span>vapor</span><!--]--><!--${slotAnchorLabel}-->` +
|
||||||
|
`<!--]-->`,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('consecutive slots with anchor insertion', async () => {
|
||||||
|
const data = reactive({
|
||||||
|
msg1: 'foo',
|
||||||
|
msg2: 'bar',
|
||||||
|
})
|
||||||
|
|
||||||
|
const { container } = await testHydration(
|
||||||
|
`<template>
|
||||||
|
<components.Child>
|
||||||
|
<span>{{data.msg1}}</span>
|
||||||
|
<template #bar>
|
||||||
|
<span>{{data.msg2}}</span>
|
||||||
|
</template>
|
||||||
|
</components.Child>
|
||||||
|
</template>`,
|
||||||
|
{
|
||||||
|
Child: `<template>
|
||||||
|
<div>
|
||||||
|
<span/>
|
||||||
|
<slot/>
|
||||||
|
<slot name="bar"/>
|
||||||
|
<span/>
|
||||||
|
</div>
|
||||||
|
</template>`,
|
||||||
|
},
|
||||||
|
data,
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(container.innerHTML).toBe(
|
||||||
|
`<div>` +
|
||||||
|
`<span></span>` +
|
||||||
|
`<!--[--><span>foo</span><!--]--><!--${slotAnchorLabel}-->` +
|
||||||
|
`<!--[--><span>bar</span><!--]--><!--${slotAnchorLabel}-->` +
|
||||||
|
`<span></span>` +
|
||||||
|
`</div>`,
|
||||||
|
)
|
||||||
|
|
||||||
|
data.msg1 = 'hello'
|
||||||
|
data.msg2 = 'vapor'
|
||||||
|
await nextTick()
|
||||||
|
expect(container.innerHTML).toBe(
|
||||||
|
`<div>` +
|
||||||
|
`<span></span>` +
|
||||||
|
`<!--[--><span>hello</span><!--]--><!--${slotAnchorLabel}-->` +
|
||||||
|
`<!--[--><span>vapor</span><!--]--><!--${slotAnchorLabel}-->` +
|
||||||
|
`<span></span>` +
|
||||||
|
`</div>`,
|
||||||
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
// test('element with ref', () => {
|
// test('element with ref', () => {
|
||||||
|
|
|
@ -19,7 +19,7 @@ import {
|
||||||
import {
|
import {
|
||||||
createComment,
|
createComment,
|
||||||
createTextNode,
|
createTextNode,
|
||||||
nextVaporFragmentAnchor,
|
findVaporFragmentAnchor,
|
||||||
} from './dom/node'
|
} from './dom/node'
|
||||||
import {
|
import {
|
||||||
type Block,
|
type Block,
|
||||||
|
@ -34,7 +34,6 @@ import { renderEffect } from './renderEffect'
|
||||||
import { VaporVForFlags } from '../../shared/src/vaporFlags'
|
import { VaporVForFlags } from '../../shared/src/vaporFlags'
|
||||||
import {
|
import {
|
||||||
currentHydrationNode,
|
currentHydrationNode,
|
||||||
isComment,
|
|
||||||
isHydrating,
|
isHydrating,
|
||||||
locateHydrationNode,
|
locateHydrationNode,
|
||||||
} from './dom/hydration'
|
} from './dom/hydration'
|
||||||
|
@ -99,15 +98,20 @@ export const createFor = (
|
||||||
let oldBlocks: ForBlock[] = []
|
let oldBlocks: ForBlock[] = []
|
||||||
let newBlocks: ForBlock[]
|
let newBlocks: ForBlock[]
|
||||||
let parent: ParentNode | undefined | null
|
let parent: ParentNode | undefined | null
|
||||||
const parentAnchor = isHydrating
|
let parentAnchor: Node
|
||||||
? // Use fragment end anchor if available, otherwise use the specific for anchor.
|
if (isHydrating) {
|
||||||
nextVaporFragmentAnchor(
|
parentAnchor = findVaporFragmentAnchor(
|
||||||
currentHydrationNode!,
|
currentHydrationNode!,
|
||||||
isComment(currentHydrationNode!, '[') ? ']' : FOR_ANCHOR_LABEL,
|
FOR_ANCHOR_LABEL,
|
||||||
)!
|
)!
|
||||||
: __DEV__
|
if (__DEV__ && !parentAnchor) {
|
||||||
? createComment('for')
|
// TODO warn, should not happen
|
||||||
: createTextNode()
|
warn(`createFor anchor not found...`)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
parentAnchor = __DEV__ ? createComment('for') : createTextNode()
|
||||||
|
}
|
||||||
|
|
||||||
const frag = new VaporFragment(oldBlocks)
|
const frag = new VaporFragment(oldBlocks)
|
||||||
const instance = currentInstance!
|
const instance = currentInstance!
|
||||||
const canUseFastRemove = flags & VaporVForFlags.FAST_REMOVE
|
const canUseFastRemove = flags & VaporVForFlags.FAST_REMOVE
|
||||||
|
|
|
@ -8,7 +8,7 @@ import {
|
||||||
import {
|
import {
|
||||||
createComment,
|
createComment,
|
||||||
createTextNode,
|
createTextNode,
|
||||||
nextVaporFragmentAnchor,
|
findVaporFragmentAnchor,
|
||||||
} from './dom/node'
|
} from './dom/node'
|
||||||
import { EffectScope, pauseTracking, resetTracking } from '@vue/reactivity'
|
import { EffectScope, pauseTracking, resetTracking } from '@vue/reactivity'
|
||||||
import {
|
import {
|
||||||
|
@ -99,7 +99,7 @@ export class DynamicFragment extends VaporFragment {
|
||||||
this.anchor = currentHydrationNode
|
this.anchor = currentHydrationNode
|
||||||
} else {
|
} else {
|
||||||
// find next sibling dynamic fragment end anchor
|
// find next sibling dynamic fragment end anchor
|
||||||
const anchor = nextVaporFragmentAnchor(currentHydrationNode!, label)!
|
const anchor = findVaporFragmentAnchor(currentHydrationNode!, label)!
|
||||||
if (anchor) {
|
if (anchor) {
|
||||||
this.anchor = anchor
|
this.anchor = anchor
|
||||||
} else if (__DEV__) {
|
} else if (__DEV__) {
|
||||||
|
|
|
@ -59,7 +59,11 @@ import {
|
||||||
} from './componentSlots'
|
} from './componentSlots'
|
||||||
import { hmrReload, hmrRerender } from './hmr'
|
import { hmrReload, hmrRerender } from './hmr'
|
||||||
import { isHydrating, locateHydrationNode } from './dom/hydration'
|
import { isHydrating, locateHydrationNode } from './dom/hydration'
|
||||||
import { insertionAnchor, insertionParent } from './insertionState'
|
import {
|
||||||
|
insertionAnchor,
|
||||||
|
insertionParent,
|
||||||
|
resetInsertionState,
|
||||||
|
} from './insertionState'
|
||||||
|
|
||||||
export { currentInstance } from '@vue/runtime-dom'
|
export { currentInstance } from '@vue/runtime-dom'
|
||||||
|
|
||||||
|
@ -142,6 +146,8 @@ export function createComponent(
|
||||||
const _insertionAnchor = insertionAnchor
|
const _insertionAnchor = insertionAnchor
|
||||||
if (isHydrating) {
|
if (isHydrating) {
|
||||||
locateHydrationNode()
|
locateHydrationNode()
|
||||||
|
} else {
|
||||||
|
resetInsertionState()
|
||||||
}
|
}
|
||||||
|
|
||||||
// vdom interop enabled and component is not an explicit vapor component
|
// vdom interop enabled and component is not an explicit vapor component
|
||||||
|
|
|
@ -6,7 +6,7 @@ import {
|
||||||
setInsertionState,
|
setInsertionState,
|
||||||
} from '../insertionState'
|
} from '../insertionState'
|
||||||
import {
|
import {
|
||||||
child,
|
_child,
|
||||||
disableHydrationNodeLookup,
|
disableHydrationNodeLookup,
|
||||||
enableHydrationNodeLookup,
|
enableHydrationNodeLookup,
|
||||||
next,
|
next,
|
||||||
|
@ -28,6 +28,7 @@ export function withHydration(container: ParentNode, fn: () => void): void {
|
||||||
if (!isOptimized) {
|
if (!isOptimized) {
|
||||||
// optimize anchor cache lookup
|
// optimize anchor cache lookup
|
||||||
;(Comment.prototype as any).$fs = undefined
|
;(Comment.prototype as any).$fs = undefined
|
||||||
|
;(Node.prototype as any).$nc = undefined
|
||||||
isOptimized = true
|
isOptimized = true
|
||||||
}
|
}
|
||||||
enableHydrationNodeLookup()
|
enableHydrationNodeLookup()
|
||||||
|
@ -87,19 +88,17 @@ function locateHydrationNodeImpl(hasFragmentAnchor?: boolean) {
|
||||||
let node: Node | null
|
let node: Node | null
|
||||||
// prepend / firstChild
|
// prepend / firstChild
|
||||||
if (insertionAnchor === 0) {
|
if (insertionAnchor === 0) {
|
||||||
node = child(insertionParent!)
|
node = _child(insertionParent!)
|
||||||
} else if (insertionAnchor) {
|
} else if (insertionAnchor) {
|
||||||
// for dynamic children, use insertionAnchor as the node
|
// for dynamic children, use insertionAnchor as the node
|
||||||
node = insertionAnchor
|
node = insertionAnchor
|
||||||
} else {
|
} else {
|
||||||
node = insertionParent ? insertionParent.lastChild : currentHydrationNode
|
node = insertionParent
|
||||||
|
? insertionParent.$nc || insertionParent.lastChild
|
||||||
|
: currentHydrationNode
|
||||||
|
|
||||||
// if current node is fragment start anchor, find the next one
|
|
||||||
if (node && isComment(node, '[')) {
|
|
||||||
node = node.nextSibling
|
|
||||||
}
|
|
||||||
// if the last child is a vapor fragment end anchor, find the previous one
|
// if the last child is a vapor fragment end anchor, find the previous one
|
||||||
else if (hasFragmentAnchor && node && isVaporFragmentEndAnchor(node)) {
|
if (hasFragmentAnchor && node && isVaporFragmentEndAnchor(node)) {
|
||||||
node = node.previousSibling
|
node = node.previousSibling
|
||||||
if (__DEV__ && !node) {
|
if (__DEV__ && !node) {
|
||||||
// TODO warning, should not happen
|
// TODO warning, should not happen
|
||||||
|
@ -135,6 +134,10 @@ function locateHydrationNodeImpl(hasFragmentAnchor?: boolean) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (insertionParent && node) {
|
||||||
|
insertionParent.$nc = node!.previousSibling
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (__DEV__ && !node) {
|
if (__DEV__ && !node) {
|
||||||
|
|
|
@ -22,10 +22,42 @@ export function querySelector(selectors: string): Element | null {
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! #__NO_SIDE_EFFECTS__ */
|
/*! #__NO_SIDE_EFFECTS__ */
|
||||||
export function child(node: ParentNode): Node {
|
export function _child(node: ParentNode): Node {
|
||||||
return node.firstChild!
|
return node.firstChild!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! #__NO_SIDE_EFFECTS__ */
|
||||||
|
export function __child(node: ParentNode): Node {
|
||||||
|
/**
|
||||||
|
* During hydration, the first child of a node not be the expected
|
||||||
|
* if the first child is slot
|
||||||
|
*
|
||||||
|
* for template code: `div><slot />{{ data }}</div>`
|
||||||
|
* - slot: 'slot',
|
||||||
|
* - data: 'hi',
|
||||||
|
*
|
||||||
|
* client side:
|
||||||
|
* const n2 = _template("<div> </div>")()
|
||||||
|
* const n1 = _child(n2) -> the text node
|
||||||
|
* _setInsertionState(n2, 0) -> slot fragment
|
||||||
|
*
|
||||||
|
* during hydration:
|
||||||
|
* const n2 = _template("<div><!--[-->slot<!--]--><!--slot-->Hi</div>")()
|
||||||
|
* const n1 = _child(n2) -> should be `Hi` instead of the slot fragment
|
||||||
|
* _setInsertionState(n2, 0) -> slot fragment
|
||||||
|
*/
|
||||||
|
let n = node.firstChild!
|
||||||
|
|
||||||
|
if (isComment(n, '[')) {
|
||||||
|
n = locateEndAnchor(n)!.nextSibling!
|
||||||
|
}
|
||||||
|
|
||||||
|
while (n && isVaporFragmentEndAnchor(n)) {
|
||||||
|
n = n.nextSibling!
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
/*! #__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]
|
return node.childNodes[i]
|
||||||
|
@ -56,9 +88,13 @@ export function __next(node: Node): Node {
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ChildFn = (node: ParentNode) => Node
|
||||||
type NextFn = (node: Node) => Node
|
type NextFn = (node: Node) => Node
|
||||||
type NthChildFn = (node: Node, i: number) => Node
|
type NthChildFn = (node: Node, i: number) => Node
|
||||||
|
|
||||||
|
interface DelegatedChildFunction extends ChildFn {
|
||||||
|
impl: ChildFn
|
||||||
|
}
|
||||||
interface DelegatedNextFunction extends NextFn {
|
interface DelegatedNextFunction extends NextFn {
|
||||||
impl: NextFn
|
impl: NextFn
|
||||||
}
|
}
|
||||||
|
@ -66,6 +102,12 @@ interface DelegatedNthChildFunction extends NthChildFn {
|
||||||
impl: NthChildFn
|
impl: NthChildFn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! #__NO_SIDE_EFFECTS__ */
|
||||||
|
export const child: DelegatedChildFunction = node => {
|
||||||
|
return child.impl(node)
|
||||||
|
}
|
||||||
|
child.impl = _child
|
||||||
|
|
||||||
/*! #__NO_SIDE_EFFECTS__ */
|
/*! #__NO_SIDE_EFFECTS__ */
|
||||||
export const next: DelegatedNextFunction = node => {
|
export const next: DelegatedNextFunction = node => {
|
||||||
return next.impl(node)
|
return next.impl(node)
|
||||||
|
@ -90,11 +132,13 @@ nthChild.impl = _nthChild
|
||||||
// of `next` and `nthChild`. After hydration is complete, their implementations
|
// of `next` and `nthChild`. After hydration is complete, their implementations
|
||||||
// are restored to the original versions.
|
// are restored to the original versions.
|
||||||
export function enableHydrationNodeLookup(): void {
|
export function enableHydrationNodeLookup(): void {
|
||||||
|
child.impl = __child
|
||||||
next.impl = __next
|
next.impl = __next
|
||||||
nthChild.impl = __nthChild
|
nthChild.impl = __nthChild
|
||||||
}
|
}
|
||||||
|
|
||||||
export function disableHydrationNodeLookup(): void {
|
export function disableHydrationNodeLookup(): void {
|
||||||
|
child.impl = _child
|
||||||
next.impl = _next
|
next.impl = _next
|
||||||
nthChild.impl = _nthChild
|
nthChild.impl = _nthChild
|
||||||
}
|
}
|
||||||
|
@ -112,15 +156,10 @@ function isNonHydrationNode(node: Node) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function nextVaporFragmentAnchor(
|
export function findVaporFragmentAnchor(
|
||||||
node: Node,
|
node: Node,
|
||||||
anchorLabel: string,
|
anchorLabel: string,
|
||||||
): Comment | null {
|
): Comment | null {
|
||||||
node = handleWrappedNode(node)
|
|
||||||
if (isComment(node, anchorLabel)) {
|
|
||||||
return node as Comment
|
|
||||||
}
|
|
||||||
|
|
||||||
let n = node.nextSibling
|
let n = node.nextSibling
|
||||||
while (n) {
|
while (n) {
|
||||||
if (isComment(n, anchorLabel)) return n
|
if (isComment(n, anchorLabel)) return n
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
export let insertionParent: ParentNode | undefined
|
export let insertionParent:
|
||||||
|
| (ParentNode & {
|
||||||
|
// the next child node to be hydrated
|
||||||
|
$nc?: Node | null
|
||||||
|
})
|
||||||
|
| undefined
|
||||||
export let insertionAnchor: Node | 0 | undefined
|
export let insertionAnchor: Node | 0 | undefined
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -104,7 +104,7 @@ export function ssrRenderSlotInner(
|
||||||
if (
|
if (
|
||||||
transition &&
|
transition &&
|
||||||
slotBuffer[0] === '<!--[-->' &&
|
slotBuffer[0] === '<!--[-->' &&
|
||||||
slotBuffer[end - 1] === '<!--]-->'
|
(slotBuffer[end - 1] as string).startsWith('<!--]-->')
|
||||||
) {
|
) {
|
||||||
start++
|
start++
|
||||||
end--
|
end--
|
||||||
|
|
Loading…
Reference in New Issue