mirror of https://github.com/vuejs/core.git
wip(vapor): reuse createApp from core
This commit is contained in:
parent
cc2439c9e6
commit
4fe05bdd74
|
@ -1,7 +1,8 @@
|
|||
import {
|
||||
type Component,
|
||||
type ComponentInternalInstance,
|
||||
type ConcreteComponent,
|
||||
type GenericComponent,
|
||||
type GenericComponentInstance,
|
||||
getComponentPublicInstance,
|
||||
validateComponentName,
|
||||
} from './component'
|
||||
|
@ -18,8 +19,7 @@ import { type Directive, validateDirectiveName } from './directives'
|
|||
import type { ElementNamespace, RootRenderFunction } from './renderer'
|
||||
import type { InjectionKey } from './apiInject'
|
||||
import { warn } from './warning'
|
||||
import { type VNode, cloneVNode, createVNode } from './vnode'
|
||||
import type { RootHydrateFunction } from './hydration'
|
||||
import type { VNode } from './vnode'
|
||||
import { devtoolsInitApp, devtoolsUnmountApp } from './devtools'
|
||||
import { NO, extend, isFunction, isObject } from '@vue/shared'
|
||||
import type { Data } from '@vue/runtime-shared'
|
||||
|
@ -95,11 +95,11 @@ export interface App<HostElement = any> {
|
|||
|
||||
// internal, but we need to expose these for the server-renderer and devtools
|
||||
_uid: number
|
||||
_component: ConcreteComponent
|
||||
_component: GenericComponent
|
||||
_props: Data | null
|
||||
_container: HostElement | null
|
||||
_context: AppContext
|
||||
_instance: ComponentInternalInstance | null
|
||||
_instance: GenericComponentInstance | null
|
||||
|
||||
/**
|
||||
* @internal custom element vnode
|
||||
|
@ -257,15 +257,30 @@ export function createAppContext(): AppContext {
|
|||
}
|
||||
|
||||
export type CreateAppFunction<HostElement> = (
|
||||
rootComponent: Component,
|
||||
rootComponent: GenericComponent,
|
||||
rootProps?: Data | null,
|
||||
) => App<HostElement>
|
||||
|
||||
let uid = 0
|
||||
|
||||
export type AppMountFn<HostElement> = (
|
||||
app: App,
|
||||
rootContainer: HostElement,
|
||||
isHydrate?: boolean,
|
||||
namespace?: boolean | ElementNamespace,
|
||||
) => GenericComponentInstance
|
||||
|
||||
export type AppUnmountFn = (app: App) => void
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export function createAppAPI<HostElement>(
|
||||
render: RootRenderFunction<HostElement>,
|
||||
hydrate?: RootHydrateFunction,
|
||||
// render: RootRenderFunction<HostElement>,
|
||||
// hydrate?: RootHydrateFunction,
|
||||
mount: AppMountFn<HostElement>,
|
||||
unmount: AppUnmountFn,
|
||||
render?: RootRenderFunction,
|
||||
): CreateAppFunction<HostElement> {
|
||||
return function createApp(rootComponent, rootProps = null) {
|
||||
if (!isFunction(rootComponent)) {
|
||||
|
@ -369,59 +384,32 @@ export function createAppAPI<HostElement>(
|
|||
},
|
||||
|
||||
mount(
|
||||
rootContainer: HostElement,
|
||||
rootContainer: HostElement & { __vue_app__?: App },
|
||||
isHydrate?: boolean,
|
||||
namespace?: boolean | ElementNamespace,
|
||||
): any {
|
||||
if (!isMounted) {
|
||||
// #5571
|
||||
if (__DEV__ && (rootContainer as any).__vue_app__) {
|
||||
if (__DEV__ && rootContainer.__vue_app__) {
|
||||
warn(
|
||||
`There is already an app instance mounted on the host container.\n` +
|
||||
` If you want to mount another app on the same host container,` +
|
||||
` you need to unmount the previous app by calling \`app.unmount()\` first.`,
|
||||
)
|
||||
}
|
||||
const vnode = app._ceVNode || createVNode(rootComponent, rootProps)
|
||||
// store app context on the root VNode.
|
||||
// this will be set on the root instance on initial mount.
|
||||
vnode.appContext = context
|
||||
|
||||
if (namespace === true) {
|
||||
namespace = 'svg'
|
||||
} else if (namespace === false) {
|
||||
namespace = undefined
|
||||
}
|
||||
|
||||
// HMR root reload
|
||||
if (__DEV__) {
|
||||
context.reload = () => {
|
||||
// casting to ElementNamespace because TS doesn't guarantee type narrowing
|
||||
// over function boundaries
|
||||
render(
|
||||
cloneVNode(vnode),
|
||||
rootContainer,
|
||||
namespace as ElementNamespace,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (isHydrate && hydrate) {
|
||||
hydrate(vnode as VNode<Node, Element>, rootContainer as any)
|
||||
} else {
|
||||
render(vnode, rootContainer, namespace)
|
||||
}
|
||||
isMounted = true
|
||||
app._container = rootContainer
|
||||
// for devtools and telemetry
|
||||
;(rootContainer as any).__vue_app__ = app
|
||||
const instance = mount(app, rootContainer, isHydrate, namespace)
|
||||
|
||||
if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
|
||||
app._instance = vnode.component
|
||||
app._instance = instance
|
||||
devtoolsInitApp(app, version)
|
||||
}
|
||||
|
||||
return getComponentPublicInstance(vnode.component!)
|
||||
isMounted = true
|
||||
app._container = rootContainer
|
||||
// for devtools and telemetry
|
||||
rootContainer.__vue_app__ = app
|
||||
|
||||
return getComponentPublicInstance(instance)
|
||||
} else if (__DEV__) {
|
||||
warn(
|
||||
`App has already been mounted.\n` +
|
||||
|
@ -449,7 +437,7 @@ export function createAppAPI<HostElement>(
|
|||
app._instance,
|
||||
ErrorCodes.APP_UNMOUNT_CLEANUP,
|
||||
)
|
||||
render(null, app._container)
|
||||
unmount(app)
|
||||
if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
|
||||
app._instance = null
|
||||
devtoolsUnmountApp(app)
|
||||
|
@ -485,7 +473,12 @@ export function createAppAPI<HostElement>(
|
|||
})
|
||||
|
||||
if (__COMPAT__) {
|
||||
installAppCompatProperties(app, context, render)
|
||||
installAppCompatProperties(
|
||||
app,
|
||||
context,
|
||||
// vapor doesn't have compat mode so this is always passed
|
||||
render!,
|
||||
)
|
||||
}
|
||||
|
||||
return app
|
||||
|
|
|
@ -367,6 +367,9 @@ export interface GenericComponentInstance {
|
|||
*/
|
||||
propsDefaults: Data | null
|
||||
|
||||
// exposed properties via expose()
|
||||
exposed: Record<string, any> | null
|
||||
|
||||
// lifecycle
|
||||
isMounted: boolean
|
||||
isUnmounted: boolean
|
||||
|
@ -519,8 +522,7 @@ export interface ComponentInternalInstance extends GenericComponentInstance {
|
|||
data: Data // options API only
|
||||
emit: EmitFn
|
||||
slots: InternalSlots
|
||||
// exposed properties via expose()
|
||||
exposed: Record<string, any> | null
|
||||
|
||||
exposeProxy: Record<string, any> | null
|
||||
|
||||
/**
|
||||
|
@ -1228,24 +1230,33 @@ export function createSetupContext(
|
|||
}
|
||||
|
||||
export function getComponentPublicInstance(
|
||||
instance: ComponentInternalInstance,
|
||||
instance: GenericComponentInstance,
|
||||
): ComponentPublicInstance | ComponentInternalInstance['exposed'] | null {
|
||||
if (instance.exposed) {
|
||||
return (
|
||||
instance.exposeProxy ||
|
||||
(instance.exposeProxy = new Proxy(proxyRefs(markRaw(instance.exposed)), {
|
||||
get(target, key: string) {
|
||||
if (key in target) {
|
||||
return target[key]
|
||||
} else if (key in publicPropertiesMap) {
|
||||
return publicPropertiesMap[key](instance)
|
||||
}
|
||||
},
|
||||
has(target, key: string) {
|
||||
return key in target || key in publicPropertiesMap
|
||||
},
|
||||
}))
|
||||
)
|
||||
if ('exposeProxy' in instance) {
|
||||
return (
|
||||
instance.exposeProxy ||
|
||||
(instance.exposeProxy = new Proxy(
|
||||
proxyRefs(markRaw(instance.exposed)),
|
||||
{
|
||||
get(target, key: string) {
|
||||
if (key in target) {
|
||||
return target[key]
|
||||
} else if (key in publicPropertiesMap) {
|
||||
return publicPropertiesMap[key](
|
||||
instance as ComponentInternalInstance,
|
||||
)
|
||||
}
|
||||
},
|
||||
has(target, key: string) {
|
||||
return key in target || key in publicPropertiesMap
|
||||
},
|
||||
},
|
||||
))
|
||||
)
|
||||
} else {
|
||||
return instance.exposed
|
||||
}
|
||||
} else {
|
||||
return instance.proxy
|
||||
}
|
||||
|
|
|
@ -501,3 +501,8 @@ export {
|
|||
nextUid,
|
||||
} from './component'
|
||||
export { pushWarningContext, popWarningContext } from './warning'
|
||||
export {
|
||||
createAppAPI,
|
||||
type AppMountFn,
|
||||
type AppUnmountFn,
|
||||
} from './apiCreateApp'
|
||||
|
|
|
@ -8,6 +8,7 @@ import {
|
|||
type VNodeHook,
|
||||
type VNodeProps,
|
||||
cloneIfMounted,
|
||||
cloneVNode,
|
||||
createVNode,
|
||||
invokeVNodeHook,
|
||||
isSameVNodeType,
|
||||
|
@ -57,7 +58,12 @@ import {
|
|||
import { updateProps } from './componentProps'
|
||||
import { updateSlots } from './componentSlots'
|
||||
import { popWarningContext, pushWarningContext, warn } from './warning'
|
||||
import { type CreateAppFunction, createAppAPI } from './apiCreateApp'
|
||||
import {
|
||||
type AppMountFn,
|
||||
type AppUnmountFn,
|
||||
type CreateAppFunction,
|
||||
createAppAPI,
|
||||
} from './apiCreateApp'
|
||||
import { setRef } from './rendererTemplateRef'
|
||||
import {
|
||||
type SuspenseBoundary,
|
||||
|
@ -2397,10 +2403,49 @@ function baseCreateRenderer(
|
|||
)
|
||||
}
|
||||
|
||||
const mountApp: AppMountFn<Element> = (
|
||||
app,
|
||||
container,
|
||||
isHydrate,
|
||||
namespace,
|
||||
) => {
|
||||
const vnode = app._ceVNode || createVNode(app._component, app._props)
|
||||
// store app context on the root VNode.
|
||||
// this will be set on the root instance on initial mount.
|
||||
vnode.appContext = app._context
|
||||
|
||||
if (namespace === true) {
|
||||
namespace = 'svg'
|
||||
} else if (namespace === false) {
|
||||
namespace = undefined
|
||||
}
|
||||
|
||||
// HMR root reload
|
||||
if (__DEV__) {
|
||||
app._context.reload = () => {
|
||||
// casting to ElementNamespace because TS doesn't guarantee type narrowing
|
||||
// over function boundaries
|
||||
render(cloneVNode(vnode), container, namespace as ElementNamespace)
|
||||
}
|
||||
}
|
||||
|
||||
if (isHydrate && hydrate) {
|
||||
hydrate(vnode as VNode<Node, Element>, container as any)
|
||||
} else {
|
||||
render(vnode, container, namespace)
|
||||
}
|
||||
|
||||
return vnode.component!
|
||||
}
|
||||
|
||||
const unmountApp: AppUnmountFn = app => {
|
||||
render(null, app._container)
|
||||
}
|
||||
|
||||
return {
|
||||
render,
|
||||
hydrate,
|
||||
createApp: createAppAPI(render, hydrate),
|
||||
createApp: createAppAPI(mountApp, unmountApp, render),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import {
|
||||
type App,
|
||||
type ConcreteComponent,
|
||||
type CreateAppFunction,
|
||||
type DefineComponent,
|
||||
DeprecationTypes,
|
||||
|
@ -108,7 +109,7 @@ export const createApp = ((...args) => {
|
|||
const container = normalizeContainer(containerOrSelector)
|
||||
if (!container) return
|
||||
|
||||
const component = app._component
|
||||
const component = app._component as ConcreteComponent
|
||||
if (!isFunction(component) && !component.render && !component.template) {
|
||||
// __UNSAFE__
|
||||
// Reason: potential execution of JS expressions in in-DOM template.
|
||||
|
@ -225,7 +226,10 @@ function injectCompilerOptionsCheck(app: App) {
|
|||
}
|
||||
}
|
||||
|
||||
function normalizeContainer(
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export function normalizeContainer(
|
||||
container: Element | ShadowRoot | string,
|
||||
): Element | ShadowRoot | null {
|
||||
if (isString(container)) {
|
||||
|
|
|
@ -1,18 +1,36 @@
|
|||
import { normalizeContainer } from '../apiRender'
|
||||
import { insert } from '../dom/element'
|
||||
import { type VaporComponent, createComponent } from './component'
|
||||
import {
|
||||
type AppMountFn,
|
||||
type AppUnmountFn,
|
||||
type CreateAppFunction,
|
||||
createAppAPI,
|
||||
} from '@vue/runtime-core'
|
||||
|
||||
let _createApp: CreateAppFunction<ParentNode>
|
||||
|
||||
const mountApp: AppMountFn<ParentNode> = (app, container) => {
|
||||
// clear content before mounting
|
||||
if (container.nodeType === 1 /* Node.ELEMENT_NODE */) {
|
||||
container.textContent = ''
|
||||
}
|
||||
const instance = createComponent(app._component)
|
||||
insert(instance.block, container)
|
||||
return instance
|
||||
}
|
||||
|
||||
const unmountApp: AppUnmountFn = app => {
|
||||
// TODO
|
||||
}
|
||||
|
||||
export function createVaporApp(comp: VaporComponent): any {
|
||||
return {
|
||||
mount(container: string | ParentNode) {
|
||||
container = normalizeContainer(container)
|
||||
// clear content before mounting
|
||||
if (container.nodeType === 1 /* Node.ELEMENT_NODE */) {
|
||||
container.textContent = ''
|
||||
}
|
||||
const instance = createComponent(comp)
|
||||
insert(instance.block, container)
|
||||
return instance
|
||||
},
|
||||
if (!_createApp) _createApp = createAppAPI(mountApp, unmountApp)
|
||||
const app = _createApp(comp)
|
||||
const mount = app.mount
|
||||
app.mount = (container, ...args: any[]) => {
|
||||
container = normalizeContainer(container) // TODO reuse from runtime-dom
|
||||
return mount(container, ...args)
|
||||
}
|
||||
return app
|
||||
}
|
||||
|
|
|
@ -143,7 +143,7 @@ export class VaporComponentInstance implements GenericComponentInstance {
|
|||
rawProps: RawProps | undefined
|
||||
props: Record<string, any>
|
||||
attrs: Record<string, any>
|
||||
exposed?: Record<string, any>
|
||||
exposed: Record<string, any> | null
|
||||
|
||||
emitted: Record<string, boolean> | null
|
||||
propsDefaults: Record<string, any> | null
|
||||
|
@ -178,7 +178,7 @@ export class VaporComponentInstance implements GenericComponentInstance {
|
|||
|
||||
this.rawProps = rawProps
|
||||
this.provides = this.refs = EMPTY_OBJ
|
||||
this.emitted = this.ec = null
|
||||
this.emitted = this.ec = this.exposed = null
|
||||
this.isMounted = this.isUnmounted = this.isDeactivated = false
|
||||
|
||||
// init props
|
||||
|
|
Loading…
Reference in New Issue