vue3-yuanma/packages/runtime-core/src/apiApp.ts

199 lines
5.0 KiB
TypeScript
Raw Normal View History

2019-09-02 20:09:34 +00:00
import {
ComponentOptions,
Component,
ComponentRenderProxy,
Data,
2019-09-02 20:43:26 +00:00
ComponentInstance,
currentRenderingInstance,
currentInstance
2019-09-02 20:09:34 +00:00
} from './component'
import { Directive } from './directives'
import { HostNode, RootRenderFunction } from './createRenderer'
import { InjectionKey } from './apiInject'
2019-09-02 20:43:26 +00:00
import { isFunction, camelize, capitalize } from '@vue/shared'
2019-09-02 20:09:34 +00:00
import { warn } from './warning'
import { createVNode } from './vnode'
export interface App {
config: AppConfig
use(plugin: Plugin, options?: any): this
mixin(mixin: ComponentOptions): this
component(name: string): Component | undefined
component(name: string, component: Component): this
directive(name: string): Directive | undefined
directive(name: string, directive: Directive): this
mount(
rootComponent: Component,
rootContainer: string | HostNode,
rootProps?: Data
): ComponentRenderProxy
provide<T>(key: InjectionKey<T> | string, value: T): void
}
export interface AppConfig {
devtools: boolean
performance: boolean
errorHandler?: (
err: Error,
2019-09-03 22:11:04 +00:00
instance: ComponentRenderProxy | null,
2019-09-02 20:09:34 +00:00
info: string
) => void
warnHandler?: (
msg: string,
2019-09-03 22:11:04 +00:00
instance: ComponentRenderProxy | null,
2019-09-02 20:09:34 +00:00
trace: string
) => void
}
export interface AppContext {
config: AppConfig
mixins: ComponentOptions[]
components: Record<string, Component>
directives: Record<string, Directive>
provides: Record<string | symbol, any>
}
type PluginInstallFunction = (app: App) => any
2019-09-03 22:11:04 +00:00
export type Plugin =
2019-09-02 20:09:34 +00:00
| PluginInstallFunction
| {
install: PluginInstallFunction
}
export function createAppContext(): AppContext {
return {
config: {
devtools: true,
performance: false,
errorHandler: undefined,
2019-09-02 20:43:26 +00:00
warnHandler: undefined
2019-09-02 20:09:34 +00:00
},
mixins: [],
components: {},
directives: {},
provides: {}
}
}
export function createAppAPI(render: RootRenderFunction): () => App {
return function createApp(): App {
const context = createAppContext()
2019-09-03 22:11:04 +00:00
let isMounted = false
2019-09-02 20:09:34 +00:00
const app: App = {
get config() {
return context.config
},
set config(v) {
2019-09-02 20:16:08 +00:00
if (__DEV__) {
warn(
`app.config cannot be replaced. Modify individual options instead.`
)
}
2019-09-02 20:09:34 +00:00
},
use(plugin: Plugin) {
if (isFunction(plugin)) {
plugin(app)
} else if (isFunction(plugin.install)) {
plugin.install(app)
} else if (__DEV__) {
warn(
`A plugin must either be a function or an object with an "install" ` +
`function.`
)
}
return app
},
mixin(mixin: ComponentOptions) {
context.mixins.push(mixin)
return app
},
component(name: string, component?: Component) {
// TODO component name validation
if (!component) {
return context.components[name] as any
} else {
context.components[name] = component
return app
}
},
directive(name: string, directive?: Directive) {
// TODO directive name validation
if (!directive) {
return context.directives[name] as any
} else {
context.directives[name] = directive
return app
}
},
2019-09-03 22:11:04 +00:00
mount(rootComponent, rootContainer, rootProps?: Data): any {
if (!isMounted) {
const vnode = createVNode(rootComponent, rootProps)
// store app context on the root VNode.
// this will be set on the root instance on initial mount.
vnode.appContext = context
render(vnode, rootContainer)
isMounted = true
return (vnode.component as ComponentInstance).renderProxy
} else if (__DEV__) {
warn(
`App has already been mounted. Create a new app instance instead.`
)
}
2019-09-02 20:09:34 +00:00
},
provide(key, value) {
if (__DEV__ && key in context.provides) {
warn(
`App already provides property with key "${key}". ` +
`It will be overwritten with the new value.`
)
}
context.provides[key as any] = value
}
}
return app
}
}
2019-09-02 20:43:26 +00:00
export function resolveAsset(type: 'components' | 'directives', name: string) {
const instance = currentRenderingInstance || currentInstance
if (instance) {
let camelized
let capitalized
2019-09-03 22:11:04 +00:00
let res
2019-09-02 20:43:26 +00:00
const local = (instance.type as any)[type]
2019-09-03 22:11:04 +00:00
if (local) {
res =
local[name] ||
local[(camelized = camelize(name))] ||
local[(capitalized = capitalize(camelized))]
}
if (!res) {
const global = instance.appContext[type]
res =
global[name] ||
global[camelized || (camelized = camelize(name))] ||
global[capitalized || capitalize(camelized)]
}
2019-09-02 20:43:26 +00:00
if (__DEV__ && !res) {
warn(`Failed to resolve ${type.slice(0, -1)}: ${name}`)
}
return res
} else if (__DEV__) {
warn(
`resolve${capitalize(type.slice(0, -1))} ` +
`can only be used in render() or setup().`
)
}
}