| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269 |
- import { NO, isFunction, isObject } from '@vue/shared'
- import {
- type Component,
- type ComponentInternalInstance,
- createComponentInstance,
- validateComponentName,
- } from './component'
- import { warn } from './warning'
- import { type Directive, version } from '.'
- import { render, setupComponent, unmountComponent } from './apiRender'
- import type { InjectionKey } from './apiInject'
- import type { RawProps } from './componentProps'
- import { validateDirectiveName } from './directives'
- export function createVaporApp(
- rootComponent: Component,
- rootProps: RawProps | null = null,
- ): App {
- if (rootProps != null && !isObject(rootProps) && !isFunction(rootProps)) {
- __DEV__ &&
- warn(`root props passed to app.mount() must be an object or function.`)
- rootProps = null
- }
- const context = createAppContext()
- const installedPlugins = new WeakSet()
- let instance: ComponentInternalInstance
- const app: App = {
- _context: context,
- version,
- get config() {
- return context.config
- },
- set config(v) {
- if (__DEV__) {
- warn(
- `app.config cannot be replaced. Modify individual options instead.`,
- )
- }
- },
- use(plugin: Plugin, ...options: any[]) {
- if (installedPlugins.has(plugin)) {
- __DEV__ && warn(`Plugin has already been applied to target app.`)
- } else if (plugin && isFunction(plugin.install)) {
- installedPlugins.add(plugin)
- plugin.install(app, ...options)
- } else if (isFunction(plugin)) {
- installedPlugins.add(plugin)
- plugin(app, ...options)
- } else if (__DEV__) {
- warn(
- `A plugin must either be a function or an object with an "install" ` +
- `function.`,
- )
- }
- return app
- },
- component(name: string, component?: Component): any {
- if (__DEV__) {
- validateComponentName(name, context.config)
- }
- if (!component) {
- return context.components[name]
- }
- if (__DEV__ && context.components[name]) {
- warn(`Component "${name}" has already been registered in target app.`)
- }
- context.components[name] = component
- return app
- },
- directive(name: string, directive?: Directive) {
- if (__DEV__) {
- validateDirectiveName(name)
- }
- if (!directive) {
- return context.directives[name] as any
- }
- if (__DEV__ && context.directives[name]) {
- warn(`Directive "${name}" has already been registered in target app.`)
- }
- context.directives[name] = directive
- return app
- },
- mount(rootContainer): any {
- if (!instance) {
- instance = createComponentInstance(
- rootComponent,
- rootProps,
- null,
- null,
- false,
- context,
- )
- setupComponent(instance)
- render(instance, rootContainer)
- return instance
- } else if (__DEV__) {
- warn(
- `App has already been mounted.\n` +
- `If you want to remount the same app, move your app creation logic ` +
- `into a factory function and create fresh app instances for each ` +
- `mount - e.g. \`const createMyApp = () => createApp(App)\``,
- )
- }
- },
- unmount() {
- if (instance) {
- unmountComponent(instance)
- } else if (__DEV__) {
- warn(`Cannot unmount an app that is not mounted.`)
- }
- },
- provide(key, value) {
- if (__DEV__ && (key as string | symbol) in context.provides) {
- warn(
- `App already provides property with key "${String(key)}". ` +
- `It will be overwritten with the new value.`,
- )
- }
- context.provides[key as string | symbol] = value
- return app
- },
- runWithContext(fn) {
- const lastApp = currentApp
- currentApp = app
- try {
- return fn()
- } finally {
- currentApp = lastApp
- }
- },
- }
- return app
- }
- export function createAppContext(): AppContext {
- return {
- app: null as any,
- config: {
- isNativeTag: NO,
- errorHandler: undefined,
- warnHandler: undefined,
- globalProperties: {},
- },
- provides: Object.create(null),
- components: {},
- directives: {},
- }
- }
- type PluginInstallFunction<Options = any[]> = Options extends unknown[]
- ? (app: App, ...options: Options) => any
- : (app: App, options: Options) => any
- export type ObjectPlugin<Options = any[]> = {
- install: PluginInstallFunction<Options>
- }
- export type FunctionPlugin<Options = any[]> = PluginInstallFunction<Options> &
- Partial<ObjectPlugin<Options>>
- export type Plugin<Options = any[]> =
- | FunctionPlugin<Options>
- | ObjectPlugin<Options>
- export interface App {
- version: string
- config: AppConfig
- use<Options extends unknown[]>(
- plugin: Plugin<Options>,
- ...options: Options
- ): this
- use<Options>(plugin: Plugin<Options>, options: Options): this
- component(name: string): Component | undefined
- component<T extends Component>(name: string, component: T): this
- directive<T = any, V = any>(name: string): Directive<T, V> | undefined
- directive<T = any, V = any>(name: string, directive: Directive<T, V>): this
- mount(
- rootContainer: ParentNode | string,
- isHydrate?: boolean,
- ): ComponentInternalInstance
- unmount(): void
- provide<T>(key: string | InjectionKey<T>, value: T): App
- runWithContext<T>(fn: () => T): T
- _context: AppContext
- }
- export interface AppConfig {
- // @private
- readonly isNativeTag: (tag: string) => boolean
- errorHandler?: (
- err: unknown,
- instance: ComponentInternalInstance | null,
- info: string,
- ) => void
- warnHandler?: (
- msg: string,
- instance: ComponentInternalInstance | null,
- trace: string,
- ) => void
- globalProperties: ComponentCustomProperties & Record<string, any>
- }
- export interface AppContext {
- app: App // for devtools
- config: AppConfig
- provides: Record<string | symbol, any>
- /**
- * Resolved component registry, only for components with mixins or extends
- * @internal
- */
- components: Record<string, Component>
- /**
- * Resolved directive registry, only for components with mixins or extends
- * @internal
- */
- directives: Record<string, Directive>
- }
- /**
- * @internal Used to identify the current app when using `inject()` within
- * `app.runWithContext()`.
- */
- export let currentApp: App | null = null
- /**
- * Custom properties added to component instances in any way and can be accessed through `this`
- *
- * @example
- * Here is an example of adding a property `$router` to every component instance:
- * ```ts
- * import { createApp } from 'vue'
- * import { Router, createRouter } from 'vue-router'
- *
- * declare module '@vue/runtime-core' {
- * interface ComponentCustomProperties {
- * $router: Router
- * }
- * }
- *
- * // effectively adding the router to every component instance
- * const app = createApp({})
- * const router = createRouter()
- * app.config.globalProperties.$router = router
- *
- * const vm = app.mount('#app')
- * // we can access the router from the instance
- * vm.$router.push('/')
- * ```
- */
- export interface ComponentCustomProperties {}
|