mirror of https://github.com/vuejs/core.git
wip(vapor): vdom in vapor interop
This commit is contained in:
parent
f09e343962
commit
c3e4f6621c
|
@ -1,6 +1,5 @@
|
|||
import {
|
||||
type Component,
|
||||
type ComponentInternalInstance,
|
||||
type ConcreteComponent,
|
||||
type Data,
|
||||
type GenericComponent,
|
||||
|
@ -17,7 +16,11 @@ import type {
|
|||
ComponentPublicInstance,
|
||||
} from './componentPublicInstance'
|
||||
import { type Directive, validateDirectiveName } from './directives'
|
||||
import type { ElementNamespace, RootRenderFunction } from './renderer'
|
||||
import type {
|
||||
ElementNamespace,
|
||||
RootRenderFunction,
|
||||
UnmountComponentFn,
|
||||
} from './renderer'
|
||||
import type { InjectionKey } from './apiInject'
|
||||
import { warn } from './warning'
|
||||
import type { VNode } from './vnode'
|
||||
|
@ -29,6 +32,7 @@ import type { NormalizedPropsOptions } from './componentProps'
|
|||
import type { ObjectEmitsOptions } from './componentEmits'
|
||||
import { ErrorCodes, callWithAsyncErrorHandling } from './errorHandling'
|
||||
import type { DefineComponent } from './apiDefineComponent'
|
||||
import type { VaporInteropInterface } from './vaporInterop'
|
||||
|
||||
export interface App<HostElement = any> {
|
||||
version: string
|
||||
|
@ -172,26 +176,6 @@ export interface AppConfig extends GenericAppConfig {
|
|||
* @deprecated use config.compilerOptions.isCustomElement
|
||||
*/
|
||||
isCustomElement?: (tag: string) => boolean
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
vapor?: VaporInVDOMInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export interface VaporInVDOMInterface {
|
||||
mount(
|
||||
vnode: VNode,
|
||||
container: any,
|
||||
anchor: any,
|
||||
parentComponent: ComponentInternalInstance | null,
|
||||
): GenericComponentInstance // VaporComponentInstance
|
||||
update(n1: VNode, n2: VNode, shouldUpdate: boolean): void
|
||||
unmount(vnode: VNode, doRemove?: boolean): void
|
||||
move(vnode: VNode, container: any, anchor: any): void
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -208,6 +192,19 @@ export interface GenericAppContext {
|
|||
* @internal
|
||||
*/
|
||||
reload?: () => void
|
||||
|
||||
/**
|
||||
* @internal vapor interop only, for creating vapor components in vdom
|
||||
*/
|
||||
vapor?: VaporInteropInterface
|
||||
/**
|
||||
* @internal vapor interop only, for creating vdom components in vapor
|
||||
*/
|
||||
vdomMount?: (component: ConcreteComponent, props?: any, slots?: any) => any
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
vdomUnmount?: UnmountComponentFn
|
||||
}
|
||||
|
||||
export interface AppContext extends GenericAppContext {
|
||||
|
|
|
@ -339,6 +339,7 @@ export interface GenericComponentInstance {
|
|||
vapor?: boolean
|
||||
uid: number
|
||||
type: GenericComponent
|
||||
root: GenericComponentInstance | null
|
||||
parent: GenericComponentInstance | null
|
||||
appContext: GenericAppContext
|
||||
/**
|
||||
|
@ -823,9 +824,15 @@ export function setupComponent(
|
|||
): Promise<void> | undefined {
|
||||
isSSR && setInSSRSetupState(isSSR)
|
||||
|
||||
const { props, children } = instance.vnode
|
||||
const { props, children, vi } = instance.vnode
|
||||
const isStateful = isStatefulComponent(instance)
|
||||
initProps(instance, props, isStateful, isSSR)
|
||||
|
||||
if (vi) {
|
||||
// Vapor interop override - use Vapor props/attrs proxy
|
||||
vi(instance)
|
||||
} else {
|
||||
initProps(instance, props, isStateful, isSSR)
|
||||
}
|
||||
initSlots(instance, children, optimized)
|
||||
|
||||
const setupResult = isStateful
|
||||
|
|
|
@ -454,7 +454,7 @@ export function updateHOCHostEl(
|
|||
{ vnode, parent }: ComponentInternalInstance,
|
||||
el: typeof vnode.el, // HostNode
|
||||
): void {
|
||||
while (parent) {
|
||||
while (parent && !parent.vapor) {
|
||||
const root = parent.subTree
|
||||
if (root.suspense && root.suspense.activeBranch === vnode) {
|
||||
root.el = vnode.el
|
||||
|
|
|
@ -498,7 +498,14 @@ export {
|
|||
type LifecycleHook,
|
||||
} from './component'
|
||||
export { type NormalizedPropsOptions } from './componentProps'
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export { type VaporInteropInterface } from './vaporInterop'
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export { type RendererInternals } from './renderer'
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
|
@ -530,7 +537,6 @@ export {
|
|||
createAppAPI,
|
||||
type AppMountFn,
|
||||
type AppUnmountFn,
|
||||
type VaporInVDOMInterface,
|
||||
} from './apiCreateApp'
|
||||
/**
|
||||
* @internal
|
||||
|
|
|
@ -65,7 +65,6 @@ import {
|
|||
type AppMountFn,
|
||||
type AppUnmountFn,
|
||||
type CreateAppFunction,
|
||||
type VaporInVDOMInterface,
|
||||
createAppAPI,
|
||||
} from './apiCreateApp'
|
||||
import { setRef } from './rendererTemplateRef'
|
||||
|
@ -96,10 +95,12 @@ import { isAsyncWrapper } from './apiAsyncComponent'
|
|||
import { isCompatEnabled } from './compat/compatConfig'
|
||||
import { DeprecationTypes } from './compat/compatConfig'
|
||||
import type { TransitionHooks } from './components/BaseTransition'
|
||||
import type { VaporInteropInterface } from './vaporInterop'
|
||||
|
||||
export interface Renderer<HostElement = RendererElement> {
|
||||
render: RootRenderFunction<HostElement>
|
||||
createApp: CreateAppFunction<HostElement>
|
||||
internals: RendererInternals
|
||||
}
|
||||
|
||||
export interface HydrationRenderer extends Renderer<Element | ShadowRoot> {
|
||||
|
@ -175,6 +176,7 @@ export interface RendererInternals<
|
|||
r: RemoveFn
|
||||
m: MoveFn
|
||||
mt: MountComponentFn
|
||||
umt: UnmountComponentFn
|
||||
mc: MountChildrenFn
|
||||
pc: PatchChildrenFn
|
||||
pbc: PatchBlockChildrenFn
|
||||
|
@ -271,6 +273,12 @@ export type MountComponentFn = (
|
|||
optimized: boolean,
|
||||
) => void
|
||||
|
||||
export type UnmountComponentFn = (
|
||||
instance: ComponentInternalInstance,
|
||||
parentSuspense: SuspenseBoundary | null,
|
||||
doRemove?: boolean,
|
||||
) => void
|
||||
|
||||
type ProcessTextOrCommentFn = (
|
||||
n1: VNode | null,
|
||||
n2: VNode,
|
||||
|
@ -1433,6 +1441,7 @@ function baseCreateRenderer(
|
|||
if (
|
||||
initialVNode.shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE ||
|
||||
(parent &&
|
||||
parent.vnode &&
|
||||
isAsyncWrapper(parent.vnode) &&
|
||||
parent.vnode.shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE)
|
||||
) {
|
||||
|
@ -2308,10 +2317,10 @@ function baseCreateRenderer(
|
|||
hostRemove(end)
|
||||
}
|
||||
|
||||
const unmountComponent = (
|
||||
instance: ComponentInternalInstance,
|
||||
parentSuspense: SuspenseBoundary | null,
|
||||
doRemove?: boolean,
|
||||
const unmountComponent: UnmountComponentFn = (
|
||||
instance,
|
||||
parentSuspense,
|
||||
doRemove,
|
||||
) => {
|
||||
if (__DEV__ && instance.type.__hmrId) {
|
||||
unregisterHMR(instance)
|
||||
|
@ -2437,6 +2446,7 @@ function baseCreateRenderer(
|
|||
m: move,
|
||||
r: remove,
|
||||
mt: mountComponent,
|
||||
umt: unmountComponent,
|
||||
mc: mountChildren,
|
||||
pc: patchChildren,
|
||||
pbc: patchBlockChildren,
|
||||
|
@ -2494,6 +2504,7 @@ function baseCreateRenderer(
|
|||
return {
|
||||
render,
|
||||
hydrate,
|
||||
internals,
|
||||
createApp: createAppAPI(
|
||||
mountApp,
|
||||
unmountApp,
|
||||
|
@ -2608,8 +2619,8 @@ export function invalidateMount(hooks: LifecycleHook | undefined): void {
|
|||
|
||||
function getVaporInterface(
|
||||
instance: ComponentInternalInstance | null,
|
||||
): VaporInVDOMInterface {
|
||||
const res = instance!.appContext.config.vapor
|
||||
): VaporInteropInterface {
|
||||
const res = instance!.appContext.vapor
|
||||
if (__DEV__ && !res) {
|
||||
warn(
|
||||
`Vapor component found in vdom tree but vapor-in-vdom interop was not installed. ` +
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
import type {
|
||||
ComponentInternalInstance,
|
||||
GenericComponentInstance,
|
||||
} from './component'
|
||||
import type { VNode } from './vnode'
|
||||
|
||||
/**
|
||||
* The vapor in vdom implementation is in runtime-vapor/src/vdomInterop.ts
|
||||
* @internal
|
||||
*/
|
||||
export interface VaporInteropInterface {
|
||||
mount(
|
||||
vnode: VNode,
|
||||
container: any,
|
||||
anchor: any,
|
||||
parentComponent: ComponentInternalInstance | null,
|
||||
): GenericComponentInstance // VaporComponentInstance
|
||||
update(n1: VNode, n2: VNode, shouldUpdate: boolean): void
|
||||
unmount(vnode: VNode, doRemove?: boolean): void
|
||||
move(vnode: VNode, container: any, anchor: any): void
|
||||
}
|
|
@ -253,6 +253,10 @@ export interface VNode<
|
|||
* @internal custom element interception hook
|
||||
*/
|
||||
ce?: (instance: ComponentInternalInstance) => void
|
||||
/**
|
||||
* @internal VDOM in Vapor interop hook
|
||||
*/
|
||||
vi?: (instance: ComponentInternalInstance) => void
|
||||
}
|
||||
|
||||
// Since v-if and v-for are the two possible ways node structure can dynamically
|
||||
|
|
|
@ -73,7 +73,7 @@ let renderer: Renderer<Element | ShadowRoot> | HydrationRenderer
|
|||
|
||||
let enabledHydration = false
|
||||
|
||||
function ensureRenderer() {
|
||||
function ensureRenderer(): Renderer<Element | ShadowRoot> {
|
||||
return (
|
||||
renderer ||
|
||||
(renderer = createRenderer<Node, Element | ShadowRoot>(rendererOptions))
|
||||
|
@ -230,7 +230,7 @@ function injectCompilerOptionsCheck(app: App) {
|
|||
/**
|
||||
* @internal
|
||||
*/
|
||||
export function normalizeContainer<T extends ParentNode>(
|
||||
function normalizeContainer<T extends ParentNode>(
|
||||
container: T | string,
|
||||
): T | null {
|
||||
if (isString(container)) {
|
||||
|
@ -313,7 +313,13 @@ export * from '@vue/runtime-core'
|
|||
export * from './jsx'
|
||||
|
||||
// VAPOR -----------------------------------------------------------------------
|
||||
// Everything below are exposed for vapor only and can change any time.
|
||||
// They are also trimmed from non-bundler builds.
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export { ensureRenderer, normalizeContainer }
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
|
|
|
@ -20,6 +20,8 @@ export type BlockFn = (...args: any[]) => Block
|
|||
export class VaporFragment {
|
||||
nodes: Block
|
||||
anchor?: Node
|
||||
insert?: (parent: ParentNode, anchor: Node | null) => void
|
||||
remove?: () => void
|
||||
|
||||
constructor(nodes: Block) {
|
||||
this.nodes = nodes
|
||||
|
@ -118,7 +120,11 @@ export function insert(
|
|||
}
|
||||
} else {
|
||||
// fragment
|
||||
insert(block.nodes, parent, anchor)
|
||||
if (block.insert) {
|
||||
block.insert(parent, anchor)
|
||||
} else {
|
||||
insert(block.nodes, parent, anchor)
|
||||
}
|
||||
if (block.anchor) insert(block.anchor, parent, anchor)
|
||||
}
|
||||
}
|
||||
|
@ -151,7 +157,11 @@ export function remove(block: Block, parent: ParentNode): void {
|
|||
}
|
||||
} else {
|
||||
// fragment
|
||||
remove(block.nodes, parent)
|
||||
if (block.remove) {
|
||||
block.remove()
|
||||
} else {
|
||||
remove(block.nodes, parent)
|
||||
}
|
||||
if (block.anchor) remove(block.anchor, parent)
|
||||
if ((block as DynamicFragment).scope) {
|
||||
;(block as DynamicFragment).scope!.stop()
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import {
|
||||
type ComponentInternalInstance,
|
||||
type ComponentInternalOptions,
|
||||
type ComponentPropsOptions,
|
||||
EffectScope,
|
||||
|
@ -135,7 +136,7 @@ export type LooseRawProps = Record<
|
|||
$?: DynamicPropsSource[]
|
||||
}
|
||||
|
||||
type LooseRawSlots = Record<string, VaporSlot | DynamicSlotSource[]> & {
|
||||
export type LooseRawSlots = Record<string, VaporSlot | DynamicSlotSource[]> & {
|
||||
$?: DynamicSlotSource[]
|
||||
}
|
||||
|
||||
|
@ -144,17 +145,23 @@ export function createComponent(
|
|||
rawProps?: LooseRawProps | null,
|
||||
rawSlots?: LooseRawSlots | null,
|
||||
isSingleRoot?: boolean,
|
||||
appContext?: GenericAppContext,
|
||||
appContext: GenericAppContext = (currentInstance &&
|
||||
currentInstance.appContext) ||
|
||||
emptyContext,
|
||||
): VaporComponentInstance {
|
||||
// check if we are the single root of the parent
|
||||
// if yes, inject parent attrs as dynamic props source
|
||||
// TODO avoid child overwriting parent
|
||||
// vdom interop enabled and component is not an explicit vapor component
|
||||
if (appContext.vdomMount && !component.__vapor) {
|
||||
return appContext.vdomMount(component as any, rawProps, rawSlots)
|
||||
}
|
||||
|
||||
if (
|
||||
isSingleRoot &&
|
||||
component.inheritAttrs !== false &&
|
||||
isVaporComponent(currentInstance) &&
|
||||
currentInstance.hasFallthrough
|
||||
) {
|
||||
// check if we are the single root of the parent
|
||||
// if yes, inject parent attrs as dynamic props source
|
||||
const attrs = currentInstance.attrs
|
||||
if (rawProps) {
|
||||
;((rawProps as RawProps).$ || ((rawProps as RawProps).$ = [])).push(
|
||||
|
@ -175,6 +182,10 @@ export function createComponent(
|
|||
if (__DEV__) {
|
||||
pushWarningContext(instance)
|
||||
startMeasure(instance, `init`)
|
||||
|
||||
// cache normalized options for dev only emit check
|
||||
instance.propsOptions = normalizePropsOptions(component)
|
||||
instance.emitsOptions = normalizeEmitsOptions(component)
|
||||
}
|
||||
|
||||
const prev = currentInstance
|
||||
|
@ -287,8 +298,10 @@ export class VaporComponentInstance implements GenericComponentInstance {
|
|||
vapor: true
|
||||
uid: number
|
||||
type: VaporComponent
|
||||
root: GenericComponentInstance | null
|
||||
parent: GenericComponentInstance | null
|
||||
children: VaporComponentInstance[] // TODO handle vdom children
|
||||
children: VaporComponentInstance[]
|
||||
vdomChildren?: ComponentInternalInstance[]
|
||||
appContext: GenericAppContext
|
||||
|
||||
block: Block
|
||||
|
@ -361,7 +374,8 @@ export class VaporComponentInstance implements GenericComponentInstance {
|
|||
this.vapor = true
|
||||
this.uid = nextUid()
|
||||
this.type = comp
|
||||
this.parent = currentInstance // TODO proper parent source when inside vdom instance
|
||||
this.parent = currentInstance
|
||||
this.root = currentInstance ? currentInstance.root : this
|
||||
this.children = []
|
||||
|
||||
if (currentInstance) {
|
||||
|
@ -418,12 +432,6 @@ export class VaporComponentInstance implements GenericComponentInstance {
|
|||
? new Proxy(rawSlots, dynamicSlotsProxyHandlers)
|
||||
: rawSlots
|
||||
: EMPTY_OBJ
|
||||
|
||||
if (__DEV__) {
|
||||
// cache normalized options for dev only emit check
|
||||
this.propsOptions = normalizePropsOptions(comp)
|
||||
this.emitsOptions = normalizeEmitsOptions(comp)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -448,8 +456,8 @@ export function isVaporComponent(
|
|||
*/
|
||||
export function createComponentWithFallback(
|
||||
comp: VaporComponent | string,
|
||||
rawProps?: RawProps | null,
|
||||
rawSlots?: RawSlots | null,
|
||||
rawProps?: LooseRawProps | null,
|
||||
rawSlots?: LooseRawSlots | null,
|
||||
isSingleRoot?: boolean,
|
||||
): HTMLElement | VaporComponentInstance {
|
||||
if (!isString(comp)) {
|
||||
|
@ -462,7 +470,7 @@ export function createComponentWithFallback(
|
|||
|
||||
if (rawProps) {
|
||||
renderEffect(() => {
|
||||
setDynamicProps(el, [resolveDynamicProps(rawProps)])
|
||||
setDynamicProps(el, [resolveDynamicProps(rawProps as RawProps)])
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -470,7 +478,7 @@ export function createComponentWithFallback(
|
|||
if (rawSlots.$) {
|
||||
// TODO dynamic slot fragment
|
||||
} else {
|
||||
insert(getSlot(rawSlots, 'default')!(), el)
|
||||
insert(getSlot(rawSlots as RawSlots, 'default')!(), el)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -517,6 +525,14 @@ export function unmountComponent(
|
|||
}
|
||||
instance.children = EMPTY_ARR as any
|
||||
|
||||
if (instance.vdomChildren) {
|
||||
const unmount = instance.appContext.vdomUnmount!
|
||||
for (const c of instance.vdomChildren) {
|
||||
unmount(c, null)
|
||||
}
|
||||
instance.vdomChildren = EMPTY_ARR as any
|
||||
}
|
||||
|
||||
if (parentNode) {
|
||||
// root remove: need to both remove this instance's DOM nodes
|
||||
// and also remove it from the parent's children list.
|
||||
|
|
|
@ -199,7 +199,9 @@ export function getAttrFromRawProps(rawProps: RawProps, key: string): unknown {
|
|||
return rawProps[key]()
|
||||
}
|
||||
}
|
||||
return merged
|
||||
if (merged && merged.length) {
|
||||
return merged
|
||||
}
|
||||
}
|
||||
|
||||
export function hasAttrFromRawProps(rawProps: RawProps, key: string): boolean {
|
||||
|
@ -342,3 +344,18 @@ function propsDeleteDevTrap(_: any, key: string | symbol) {
|
|||
)
|
||||
return true
|
||||
}
|
||||
|
||||
export const rawPropsProxyHandlers: ProxyHandler<RawProps> = {
|
||||
get: getAttrFromRawProps,
|
||||
has: hasAttrFromRawProps,
|
||||
ownKeys: getKeysFromRawProps,
|
||||
getOwnPropertyDescriptor(target, key: string) {
|
||||
if (hasAttrFromRawProps(target, key)) {
|
||||
return {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
get: () => getAttrFromRawProps(target, key),
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
|
@ -1,11 +1,6 @@
|
|||
import { EMPTY_OBJ, NO, hasOwn, isArray, isFunction } from '@vue/shared'
|
||||
import { type Block, type BlockFn, DynamicFragment } from './block'
|
||||
import {
|
||||
type RawProps,
|
||||
getAttrFromRawProps,
|
||||
getKeysFromRawProps,
|
||||
hasAttrFromRawProps,
|
||||
} from './componentProps'
|
||||
import { rawPropsProxyHandlers } from './componentProps'
|
||||
import { currentInstance } from '@vue/runtime-core'
|
||||
import type { LooseRawProps, VaporComponentInstance } from './component'
|
||||
import { renderEffect } from './renderEffect'
|
||||
|
@ -90,21 +85,6 @@ export function getSlot(
|
|||
}
|
||||
}
|
||||
|
||||
const dynamicSlotsPropsProxyHandlers: ProxyHandler<RawProps> = {
|
||||
get: getAttrFromRawProps,
|
||||
has: hasAttrFromRawProps,
|
||||
ownKeys: getKeysFromRawProps,
|
||||
getOwnPropertyDescriptor(target, key: string) {
|
||||
if (hasAttrFromRawProps(target, key)) {
|
||||
return {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
get: () => getAttrFromRawProps(target, key),
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// TODO how to handle empty slot return blocks?
|
||||
// e.g. a slot renders a v-if node that may toggle inside.
|
||||
// we may need special handling by passing the fallback into the slot
|
||||
|
@ -119,7 +99,7 @@ export function createSlot(
|
|||
const isDynamicName = isFunction(name)
|
||||
const fragment = __DEV__ ? new DynamicFragment('slot') : new DynamicFragment()
|
||||
const slotProps = rawProps
|
||||
? new Proxy(rawProps, dynamicSlotsPropsProxyHandlers)
|
||||
? new Proxy(rawProps, rawPropsProxyHandlers)
|
||||
: EMPTY_OBJ
|
||||
|
||||
const renderSlot = () => {
|
||||
|
|
|
@ -1,19 +1,29 @@
|
|||
import {
|
||||
type ComponentInternalInstance,
|
||||
type ConcreteComponent,
|
||||
type Plugin,
|
||||
type VaporInVDOMInterface,
|
||||
type RendererInternals,
|
||||
type VaporInteropInterface,
|
||||
createVNode,
|
||||
currentInstance,
|
||||
ensureRenderer,
|
||||
shallowRef,
|
||||
simpleSetCurrentInstance,
|
||||
} from '@vue/runtime-dom'
|
||||
import {
|
||||
type VaporComponentInstance,
|
||||
type LooseRawProps,
|
||||
type LooseRawSlots,
|
||||
VaporComponentInstance,
|
||||
createComponent,
|
||||
mountComponent,
|
||||
unmountComponent,
|
||||
} from './component'
|
||||
import { insert } from './block'
|
||||
import { VaporFragment, insert } from './block'
|
||||
import { extend, remove } from '@vue/shared'
|
||||
import { type RawProps, rawPropsProxyHandlers } from './componentProps'
|
||||
import type { RawSlots } from './componentSlots'
|
||||
|
||||
const vaporInVDOMInterface: VaporInVDOMInterface = {
|
||||
const vaporInteropImpl: VaporInteropInterface = {
|
||||
mount(vnode, container, anchor, parentComponent) {
|
||||
const selfAnchor = (vnode.anchor = document.createComment('vapor'))
|
||||
container.insertBefore(selfAnchor, anchor)
|
||||
|
@ -49,6 +59,63 @@ const vaporInVDOMInterface: VaporInVDOMInterface = {
|
|||
},
|
||||
}
|
||||
|
||||
export const vaporInteropPlugin: Plugin = app => {
|
||||
app.config.vapor = vaporInVDOMInterface
|
||||
function createVDOMComponent(
|
||||
internals: RendererInternals,
|
||||
component: ConcreteComponent,
|
||||
rawProps?: LooseRawProps | null,
|
||||
rawSlots?: LooseRawSlots | null,
|
||||
): VaporFragment {
|
||||
const frag = new VaporFragment([])
|
||||
const vnode = createVNode(
|
||||
component,
|
||||
rawProps && new Proxy(rawProps, rawPropsProxyHandlers),
|
||||
)
|
||||
const wrapper = new VaporComponentInstance(
|
||||
{ props: component.props },
|
||||
rawProps as RawProps,
|
||||
rawSlots as RawSlots,
|
||||
)
|
||||
|
||||
// overwrite how the vdom instance handles props
|
||||
vnode.vi = (instance: ComponentInternalInstance) => {
|
||||
instance.props = wrapper.props
|
||||
instance.attrs = wrapper.attrs
|
||||
// TODO slots
|
||||
}
|
||||
|
||||
let isMounted = false
|
||||
const parentInstance = currentInstance as VaporComponentInstance
|
||||
frag.insert = (parent, anchor) => {
|
||||
if (!isMounted) {
|
||||
internals.mt(
|
||||
vnode,
|
||||
parent,
|
||||
anchor,
|
||||
parentInstance as any,
|
||||
null,
|
||||
undefined,
|
||||
false,
|
||||
)
|
||||
;(parentInstance.vdomChildren || (parentInstance.vdomChildren = [])).push(
|
||||
vnode.component!,
|
||||
)
|
||||
isMounted = true
|
||||
} else {
|
||||
// TODO move
|
||||
}
|
||||
}
|
||||
frag.remove = () => {
|
||||
internals.umt(vnode.component!, null, true)
|
||||
remove(parentInstance.vdomChildren!, vnode.component)
|
||||
isMounted = false
|
||||
}
|
||||
|
||||
return frag
|
||||
}
|
||||
|
||||
export const vaporInteropPlugin: Plugin = app => {
|
||||
app._context.vapor = extend(vaporInteropImpl)
|
||||
const internals = ensureRenderer().internals
|
||||
app._context.vdomMount = createVDOMComponent.bind(null, internals)
|
||||
app._context.vdomUnmount = internals.umt
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue