apiCreateVaporApp.ts 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. import { isObject } from '@vue/shared'
  2. import {
  3. type Component,
  4. type ComponentInternalInstance,
  5. createComponentInstance,
  6. } from './component'
  7. import { warn } from './warning'
  8. import { version } from '.'
  9. import { render, setupComponent, unmountComponent } from './apiRender'
  10. import type { InjectionKey } from './apiInject'
  11. import type { RawProps } from './componentProps'
  12. export function createVaporApp(
  13. rootComponent: Component,
  14. rootProps: RawProps | null = null,
  15. ): App {
  16. if (rootProps != null && !isObject(rootProps)) {
  17. __DEV__ && warn(`root props passed to app.mount() must be an object.`)
  18. rootProps = null
  19. }
  20. const context = createAppContext()
  21. let instance: ComponentInternalInstance
  22. const app: App = {
  23. _context: context,
  24. version,
  25. get config() {
  26. return context.config
  27. },
  28. set config(v) {
  29. if (__DEV__) {
  30. warn(
  31. `app.config cannot be replaced. Modify individual options instead.`,
  32. )
  33. }
  34. },
  35. mount(rootContainer): any {
  36. if (!instance) {
  37. instance = createComponentInstance(
  38. rootComponent,
  39. rootProps,
  40. null,
  41. null,
  42. context,
  43. )
  44. setupComponent(instance)
  45. render(instance, rootContainer)
  46. return instance
  47. } else if (__DEV__) {
  48. warn(
  49. `App has already been mounted.\n` +
  50. `If you want to remount the same app, move your app creation logic ` +
  51. `into a factory function and create fresh app instances for each ` +
  52. `mount - e.g. \`const createMyApp = () => createApp(App)\``,
  53. )
  54. }
  55. },
  56. unmount() {
  57. if (instance) {
  58. unmountComponent(instance)
  59. } else if (__DEV__) {
  60. warn(`Cannot unmount an app that is not mounted.`)
  61. }
  62. },
  63. provide(key, value) {
  64. if (__DEV__ && (key as string | symbol) in context.provides) {
  65. warn(
  66. `App already provides property with key "${String(key)}". ` +
  67. `It will be overwritten with the new value.`,
  68. )
  69. }
  70. context.provides[key as string | symbol] = value
  71. return app
  72. },
  73. runWithContext(fn) {
  74. const lastApp = currentApp
  75. currentApp = app
  76. try {
  77. return fn()
  78. } finally {
  79. currentApp = lastApp
  80. }
  81. },
  82. }
  83. return app
  84. }
  85. export function createAppContext(): AppContext {
  86. return {
  87. app: null as any,
  88. config: {
  89. errorHandler: undefined,
  90. warnHandler: undefined,
  91. },
  92. provides: Object.create(null),
  93. }
  94. }
  95. export interface App {
  96. version: string
  97. config: AppConfig
  98. mount(
  99. rootContainer: ParentNode | string,
  100. isHydrate?: boolean,
  101. ): ComponentInternalInstance
  102. unmount(): void
  103. provide<T>(key: string | InjectionKey<T>, value: T): App
  104. runWithContext<T>(fn: () => T): T
  105. _context: AppContext
  106. }
  107. export interface AppConfig {
  108. errorHandler?: (
  109. err: unknown,
  110. instance: ComponentInternalInstance | null,
  111. info: string,
  112. ) => void
  113. warnHandler?: (
  114. msg: string,
  115. instance: ComponentInternalInstance | null,
  116. trace: string,
  117. ) => void
  118. }
  119. export interface AppContext {
  120. app: App // for devtools
  121. config: AppConfig
  122. provides: Record<string | symbol, any>
  123. }
  124. /**
  125. * @internal Used to identify the current app when using `inject()` within
  126. * `app.runWithContext()`.
  127. */
  128. export let currentApp: App | null = null