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