mirror of https://github.com/vuejs/core.git
wip: vdom interop
This commit is contained in:
parent
d281d62312
commit
ea34f2f555
|
@ -33,7 +33,6 @@ import type { NormalizedPropsOptions } from './componentProps'
|
|||
import type { ObjectEmitsOptions } from './componentEmits'
|
||||
import { ErrorCodes, callWithAsyncErrorHandling } from './errorHandling'
|
||||
import type { DefineComponent } from './apiDefineComponent'
|
||||
import type { createHydrationFunctions } from './hydration'
|
||||
|
||||
export interface App<HostElement = any> {
|
||||
version: string
|
||||
|
@ -105,7 +104,6 @@ export interface App<HostElement = any> {
|
|||
_container: HostElement | null
|
||||
_context: AppContext
|
||||
_instance: GenericComponentInstance | null
|
||||
_ssr?: boolean
|
||||
|
||||
/**
|
||||
* @internal custom element vnode
|
||||
|
@ -206,7 +204,6 @@ export interface VaporInteropInterface {
|
|||
parentComponent: any, // VaporComponentInstance
|
||||
fallback?: any, // VaporSlot
|
||||
) => any
|
||||
vdomHydrate: ReturnType<typeof createHydrationFunctions>[1] | undefined
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -149,7 +149,6 @@ export const createApp = ((...args) => {
|
|||
|
||||
export const createSSRApp = ((...args) => {
|
||||
const app = ensureHydrationRenderer().createApp(...args)
|
||||
app._ssr = true
|
||||
|
||||
if (__DEV__) {
|
||||
injectNativeTagCheck(app)
|
||||
|
|
|
@ -54,6 +54,14 @@ function compile(
|
|||
)
|
||||
}
|
||||
|
||||
async function testHydrationInterop(
|
||||
code: string,
|
||||
components?: Record<string, string | { code: string; vapor: boolean }>,
|
||||
data?: any,
|
||||
) {
|
||||
return testHydration(code, components, data, { interop: true, vapor: false })
|
||||
}
|
||||
|
||||
async function testHydration(
|
||||
code: string,
|
||||
components: Record<string, string | { code: string; vapor: boolean }> = {},
|
||||
|
@ -65,7 +73,7 @@ async function testHydration(
|
|||
for (const key in components) {
|
||||
const comp = components[key]
|
||||
const code = isString(comp) ? comp : comp.code
|
||||
const isVaporComp = !isString(comp) ? comp.vapor : true
|
||||
const isVaporComp = isString(comp) || !!comp.vapor
|
||||
clientComponents[key] = compile(code, data, clientComponents, {
|
||||
vapor: isVaporComp,
|
||||
ssr: false,
|
||||
|
@ -3838,9 +3846,9 @@ describe('Vapor Mode hydration', () => {
|
|||
})
|
||||
|
||||
describe('VDOM hydration interop', () => {
|
||||
test('basic component', async () => {
|
||||
test('basic vapor component', async () => {
|
||||
const data = ref(true)
|
||||
const { container } = await testHydration(
|
||||
const { container } = await testHydrationInterop(
|
||||
`<script setup>const data = _data; const components = _components;</script>
|
||||
<template>
|
||||
<components.VaporChild/>
|
||||
|
@ -3852,7 +3860,6 @@ describe('VDOM hydration interop', () => {
|
|||
},
|
||||
},
|
||||
data,
|
||||
{ interop: true, vapor: false },
|
||||
)
|
||||
|
||||
expect(container.innerHTML).toMatchInlineSnapshot(`"true"`)
|
||||
|
@ -3864,7 +3871,7 @@ describe('VDOM hydration interop', () => {
|
|||
|
||||
test('nested components (VDOM -> Vapor -> VDOM)', async () => {
|
||||
const data = ref(true)
|
||||
const { container } = await testHydration(
|
||||
const { container } = await testHydrationInterop(
|
||||
`<script setup>const data = _data; const components = _components;</script>
|
||||
<template>
|
||||
<components.VaporChild/>
|
||||
|
@ -3881,7 +3888,6 @@ describe('VDOM hydration interop', () => {
|
|||
},
|
||||
},
|
||||
data,
|
||||
{ interop: true, vapor: false },
|
||||
)
|
||||
|
||||
expect(container.innerHTML).toMatchInlineSnapshot(`"true"`)
|
||||
|
@ -3891,9 +3897,9 @@ describe('VDOM hydration interop', () => {
|
|||
expect(container.innerHTML).toMatchInlineSnapshot(`"false"`)
|
||||
})
|
||||
|
||||
test.todo('slots', async () => {
|
||||
test('vapor slot render vdom component', async () => {
|
||||
const data = ref(true)
|
||||
const { container } = await testHydration(
|
||||
const { container } = await testHydrationInterop(
|
||||
`<script setup>const data = _data; const components = _components;</script>
|
||||
<template>
|
||||
<components.VaporChild>
|
||||
|
@ -3912,7 +3918,6 @@ describe('VDOM hydration interop', () => {
|
|||
},
|
||||
},
|
||||
data,
|
||||
{ interop: true, vapor: false },
|
||||
)
|
||||
|
||||
expect(container.innerHTML).toMatchInlineSnapshot(
|
||||
|
|
|
@ -154,7 +154,6 @@ export function insert(
|
|||
} else {
|
||||
// fragment
|
||||
if (block.insert) {
|
||||
// TODO handle hydration for vdom interop
|
||||
block.insert(parent, anchor)
|
||||
} else {
|
||||
insert(block.nodes, parent, anchor)
|
||||
|
|
|
@ -58,11 +58,7 @@ import {
|
|||
getSlot,
|
||||
} from './componentSlots'
|
||||
import { hmrReload, hmrRerender } from './hmr'
|
||||
import {
|
||||
currentHydrationNode,
|
||||
isHydrating,
|
||||
locateHydrationNode,
|
||||
} from './dom/hydration'
|
||||
import { isHydrating, locateHydrationNode } from './dom/hydration'
|
||||
import {
|
||||
insertionAnchor,
|
||||
insertionParent,
|
||||
|
@ -156,22 +152,15 @@ export function createComponent(
|
|||
|
||||
// vdom interop enabled and component is not an explicit vapor component
|
||||
if (appContext.vapor && !component.__vapor) {
|
||||
const [frag, vnode] = appContext.vapor.vdomMount(
|
||||
const frag = appContext.vapor.vdomMount(
|
||||
component as any,
|
||||
rawProps,
|
||||
rawSlots,
|
||||
)
|
||||
if (!isHydrating && _insertionParent) {
|
||||
|
||||
// `frag.insert` handles both hydration and mounting
|
||||
if (_insertionParent) {
|
||||
insert(frag, _insertionParent, _insertionAnchor)
|
||||
} else if (isHydrating) {
|
||||
appContext.vapor.vdomHydrate!(
|
||||
currentHydrationNode!,
|
||||
vnode,
|
||||
currentInstance as any,
|
||||
null,
|
||||
null,
|
||||
false,
|
||||
)
|
||||
}
|
||||
return frag
|
||||
}
|
||||
|
|
|
@ -114,7 +114,6 @@ export function createSlot(
|
|||
: EMPTY_OBJ
|
||||
|
||||
let fragment: DynamicFragment
|
||||
|
||||
if (isRef(rawSlots._)) {
|
||||
fragment = instance.appContext.vapor!.vdomSlot(
|
||||
rawSlots._,
|
||||
|
@ -157,7 +156,12 @@ export function createSlot(
|
|||
}
|
||||
}
|
||||
|
||||
if (!isHydrating && _insertionParent) {
|
||||
if (
|
||||
_insertionParent &&
|
||||
(!isHydrating ||
|
||||
// for vdom interop fragment, `fragment.insert` handles both hydration and mounting
|
||||
fragment.insert)
|
||||
) {
|
||||
insert(fragment, _insertionParent, _insertionAnchor)
|
||||
}
|
||||
|
||||
|
|
|
@ -35,12 +35,17 @@ import type { RawSlots, VaporSlot } from './componentSlots'
|
|||
import { renderEffect } from './renderEffect'
|
||||
import { createTextNode } from './dom/node'
|
||||
import { optimizePropertyLookup } from './dom/prop'
|
||||
import { hydrateNode as vaporHydrateNode } from './dom/hydration'
|
||||
import {
|
||||
currentHydrationNode,
|
||||
isHydrating,
|
||||
locateHydrationNode,
|
||||
hydrateNode as vaporHydrateNode,
|
||||
} from './dom/hydration'
|
||||
|
||||
// mounting vapor components and slots in vdom
|
||||
const vaporInteropImpl: Omit<
|
||||
VaporInteropInterface,
|
||||
'vdomMount' | 'vdomUnmount' | 'vdomSlot' | 'vdomHydrate'
|
||||
'vdomMount' | 'vdomUnmount' | 'vdomSlot'
|
||||
> = {
|
||||
mount(vnode, container, anchor, parentComponent) {
|
||||
const selfAnchor = (vnode.el = vnode.anchor = createTextNode())
|
||||
|
@ -144,6 +149,8 @@ const vaporSlotsProxyHandler: ProxyHandler<any> = {
|
|||
},
|
||||
}
|
||||
|
||||
let vdomHydrateNode: HydrationRenderer['hydrateNode'] | undefined
|
||||
|
||||
/**
|
||||
* Mount vdom component in vapor
|
||||
*/
|
||||
|
@ -152,7 +159,7 @@ function createVDOMComponent(
|
|||
component: ConcreteComponent,
|
||||
rawProps?: LooseRawProps | null,
|
||||
rawSlots?: LooseRawSlots | null,
|
||||
): [VaporFragment, VNode] {
|
||||
): VaporFragment {
|
||||
const frag = new VaporFragment([])
|
||||
const vnode = createVNode(
|
||||
component,
|
||||
|
@ -181,16 +188,30 @@ function createVDOMComponent(
|
|||
}
|
||||
|
||||
frag.insert = (parentNode, anchor) => {
|
||||
if (!isMounted) {
|
||||
internals.mt(
|
||||
vnode,
|
||||
parentNode,
|
||||
anchor,
|
||||
parentInstance as any,
|
||||
null,
|
||||
undefined,
|
||||
false,
|
||||
)
|
||||
if (!isMounted || isHydrating) {
|
||||
if (isHydrating) {
|
||||
;(
|
||||
vdomHydrateNode ||
|
||||
(vdomHydrateNode = ensureHydrationRenderer().hydrateNode!)
|
||||
)(
|
||||
currentHydrationNode!,
|
||||
vnode,
|
||||
parentInstance as any,
|
||||
null,
|
||||
null,
|
||||
false,
|
||||
)
|
||||
} else {
|
||||
internals.mt(
|
||||
vnode,
|
||||
parentNode,
|
||||
anchor,
|
||||
parentInstance as any,
|
||||
null,
|
||||
undefined,
|
||||
false,
|
||||
)
|
||||
}
|
||||
onScopeDispose(unmount, true)
|
||||
isMounted = true
|
||||
} else {
|
||||
|
@ -207,7 +228,7 @@ function createVDOMComponent(
|
|||
|
||||
frag.remove = unmount
|
||||
|
||||
return [frag, vnode]
|
||||
return frag
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -235,28 +256,43 @@ function renderVDOMSlot(
|
|||
isFunction(name) ? name() : name,
|
||||
props,
|
||||
)
|
||||
if ((vnode.children as any[]).length) {
|
||||
if (fallbackNodes) {
|
||||
remove(fallbackNodes, parentNode)
|
||||
fallbackNodes = undefined
|
||||
}
|
||||
internals.p(
|
||||
oldVNode,
|
||||
if (isHydrating) {
|
||||
locateHydrationNode(true)
|
||||
;(
|
||||
vdomHydrateNode ||
|
||||
(vdomHydrateNode = ensureHydrationRenderer().hydrateNode!)
|
||||
)(
|
||||
currentHydrationNode!,
|
||||
vnode,
|
||||
parentNode,
|
||||
anchor,
|
||||
parentComponent as any,
|
||||
null,
|
||||
null,
|
||||
false,
|
||||
)
|
||||
oldVNode = vnode
|
||||
} else {
|
||||
if (fallback && !fallbackNodes) {
|
||||
// mount fallback
|
||||
if (oldVNode) {
|
||||
internals.um(oldVNode, parentComponent as any, null, true)
|
||||
if ((vnode.children as any[]).length) {
|
||||
if (fallbackNodes) {
|
||||
remove(fallbackNodes, parentNode)
|
||||
fallbackNodes = undefined
|
||||
}
|
||||
insert((fallbackNodes = fallback(props)), parentNode, anchor)
|
||||
internals.p(
|
||||
oldVNode,
|
||||
vnode,
|
||||
parentNode,
|
||||
anchor,
|
||||
parentComponent as any,
|
||||
)
|
||||
oldVNode = vnode
|
||||
} else {
|
||||
if (fallback && !fallbackNodes) {
|
||||
// mount fallback
|
||||
if (oldVNode) {
|
||||
internals.um(oldVNode, parentComponent as any, null, true)
|
||||
}
|
||||
insert((fallbackNodes = fallback(props)), parentNode, anchor)
|
||||
}
|
||||
oldVNode = null
|
||||
}
|
||||
oldVNode = null
|
||||
}
|
||||
})
|
||||
isMounted = true
|
||||
|
@ -284,14 +320,11 @@ function renderVDOMSlot(
|
|||
}
|
||||
|
||||
export const vaporInteropPlugin: Plugin = app => {
|
||||
const { internals, hydrateNode } = (
|
||||
app._ssr ? ensureHydrationRenderer() : ensureRenderer()
|
||||
) as HydrationRenderer
|
||||
const internals = ensureRenderer().internals
|
||||
app._context.vapor = extend(vaporInteropImpl, {
|
||||
vdomMount: createVDOMComponent.bind(null, internals),
|
||||
vdomUnmount: internals.umt,
|
||||
vdomSlot: renderVDOMSlot.bind(null, internals),
|
||||
vdomHydrate: hydrateNode,
|
||||
})
|
||||
const mount = app.mount
|
||||
app.mount = ((...args) => {
|
||||
|
|
Loading…
Reference in New Issue