wip: vdom hydration interop
ci / test (push) Waiting to run Details
ci / continuous-release (push) Waiting to run Details

This commit is contained in:
daiwei 2025-04-29 09:22:45 +08:00
parent 4253b0ce3e
commit d281d62312
1 changed files with 124 additions and 14 deletions

View File

@ -9,6 +9,7 @@ import {
FOR_ANCHOR_LABEL, FOR_ANCHOR_LABEL,
IF_ANCHOR_LABEL, IF_ANCHOR_LABEL,
SLOT_ANCHOR_LABEL, SLOT_ANCHOR_LABEL,
isString,
} from '@vue/shared' } from '@vue/shared'
const Vue = { ...runtimeDom, ...runtimeVapor } const Vue = { ...runtimeDom, ...runtimeVapor }
@ -17,7 +18,7 @@ function compile(
sfc: string, sfc: string,
data: runtimeDom.Ref<any>, data: runtimeDom.Ref<any>,
components: Record<string, any> = {}, components: Record<string, any> = {},
ssr = false, { vapor = true, ssr = false } = {},
) { ) {
if (!sfc.includes(`<script`)) { if (!sfc.includes(`<script`)) {
sfc = sfc =
@ -31,7 +32,7 @@ function compile(
isProd: true, isProd: true,
inlineTemplate: true, inlineTemplate: true,
genDefaultAs: '__sfc__', genDefaultAs: '__sfc__',
vapor: true, vapor,
templateOptions: { templateOptions: {
ssr, ssr,
}, },
@ -55,17 +56,27 @@ function compile(
async function testHydration( async function testHydration(
code: string, code: string,
components: Record<string, string> = {}, components: Record<string, string | { code: string; vapor: boolean }> = {},
data: any = ref('foo'), data: any = ref('foo'),
{ interop = false, vapor = true } = {},
) { ) {
const ssrComponents: any = {} const ssrComponents: any = {}
const clientComponents: any = {} const clientComponents: any = {}
for (const key in components) { for (const key in components) {
clientComponents[key] = compile(components[key], data, clientComponents) const comp = components[key]
ssrComponents[key] = compile(components[key], data, ssrComponents, true) const code = isString(comp) ? comp : comp.code
const isVaporComp = !isString(comp) ? comp.vapor : true
clientComponents[key] = compile(code, data, clientComponents, {
vapor: isVaporComp,
ssr: false,
})
ssrComponents[key] = compile(code, data, ssrComponents, {
vapor: isVaporComp,
ssr: true,
})
} }
const serverComp = compile(code, data, ssrComponents, true) const serverComp = compile(code, data, ssrComponents, { vapor, ssr: true })
const html = await VueServerRenderer.renderToString( const html = await VueServerRenderer.renderToString(
runtimeDom.createSSRApp(serverComp), runtimeDom.createSSRApp(serverComp),
) )
@ -73,8 +84,17 @@ async function testHydration(
document.body.appendChild(container) document.body.appendChild(container)
container.innerHTML = html container.innerHTML = html
const clientComp = compile(code, data, clientComponents) const clientComp = compile(code, data, clientComponents, {
const app = createVaporSSRApp(clientComp) vapor,
ssr: false,
})
let app
if (interop) {
app = runtimeDom.createSSRApp(clientComp)
app.use(runtimeVapor.vaporInteropPlugin)
} else {
app = createVaporSSRApp(clientComp)
}
app.mount(container) app.mount(container)
return { data, container } return { data, container }
} }
@ -84,13 +104,13 @@ const triggerEvent = (type: string, el: Element) => {
el.dispatchEvent(event) el.dispatchEvent(event)
} }
describe('Vapor Mode hydration', () => {
delegateEvents('click') delegateEvents('click')
beforeEach(() => { beforeEach(() => {
document.body.innerHTML = '' document.body.innerHTML = ''
}) })
describe('Vapor Mode hydration', () => {
describe('text', () => { describe('text', () => {
test('root text', async () => { test('root text', async () => {
const { data, container } = await testHydration(` const { data, container } = await testHydration(`
@ -3816,3 +3836,93 @@ describe('Vapor Mode hydration', () => {
test.todo('Teleport') test.todo('Teleport')
test.todo('Suspense') test.todo('Suspense')
}) })
describe('VDOM hydration interop', () => {
test('basic component', async () => {
const data = ref(true)
const { container } = await testHydration(
`<script setup>const data = _data; const components = _components;</script>
<template>
<components.VaporChild/>
</template>`,
{
VaporChild: {
code: `<template>{{ data }}</template>`,
vapor: true,
},
},
data,
{ interop: true, vapor: false },
)
expect(container.innerHTML).toMatchInlineSnapshot(`"true"`)
data.value = false
await nextTick()
expect(container.innerHTML).toMatchInlineSnapshot(`"false"`)
})
test('nested components (VDOM -> Vapor -> VDOM)', async () => {
const data = ref(true)
const { container } = await testHydration(
`<script setup>const data = _data; const components = _components;</script>
<template>
<components.VaporChild/>
</template>`,
{
VaporChild: {
code: `<template><components.VdomChild/></template>`,
vapor: true,
},
VdomChild: {
code: `<script setup>const data = _data;</script>
<template>{{ data }}</template>`,
vapor: false,
},
},
data,
{ interop: true, vapor: false },
)
expect(container.innerHTML).toMatchInlineSnapshot(`"true"`)
data.value = false
await nextTick()
expect(container.innerHTML).toMatchInlineSnapshot(`"false"`)
})
test.todo('slots', async () => {
const data = ref(true)
const { container } = await testHydration(
`<script setup>const data = _data; const components = _components;</script>
<template>
<components.VaporChild>
<components.VdomChild/>
</components.VaporChild>
</template>`,
{
VaporChild: {
code: `<template><div><slot/></div></template>`,
vapor: true,
},
VdomChild: {
code: `<script setup>const data = _data;</script>
<template>{{ data }}</template>`,
vapor: false,
},
},
data,
{ interop: true, vapor: false },
)
expect(container.innerHTML).toMatchInlineSnapshot(
`"<div><!--[-->true<!--]--><!--slot--></div>"`,
)
data.value = false
await nextTick()
expect(container.innerHTML).toMatchInlineSnapshot(
`"<div><!--[-->false<!--]--><!--slot--></div>"`,
)
})
})