2021-04-05 21:09:22 +00:00
|
|
|
import { reactive } from '@vue/reactivity'
|
2021-04-05 22:13:29 +00:00
|
|
|
import { isFunction } from '@vue/shared'
|
|
|
|
import { warn } from '../warning'
|
|
|
|
import { cloneVNode, createVNode } from '../vnode'
|
|
|
|
import { RootRenderFunction } from '../renderer'
|
|
|
|
import { RootHydrateFunction } from '../hydration'
|
|
|
|
import {
|
|
|
|
App,
|
|
|
|
AppConfig,
|
|
|
|
AppContext,
|
|
|
|
CreateAppFunction,
|
|
|
|
Plugin
|
|
|
|
} from '../apiCreateApp'
|
2021-04-05 21:09:22 +00:00
|
|
|
import { defineComponent } from '../apiDefineComponent'
|
2021-04-05 22:13:29 +00:00
|
|
|
import {
|
|
|
|
Component,
|
|
|
|
ComponentOptions,
|
|
|
|
createComponentInstance,
|
|
|
|
finishComponentSetup,
|
|
|
|
isRuntimeOnly,
|
|
|
|
setupComponent
|
|
|
|
} from '../component'
|
2021-04-05 21:09:22 +00:00
|
|
|
import { RenderFunction } from '../componentOptions'
|
|
|
|
import { ComponentPublicInstance } from '../componentPublicInstance'
|
2021-04-05 22:13:29 +00:00
|
|
|
import { devtoolsInitApp } from '../devtools'
|
2021-04-05 21:09:22 +00:00
|
|
|
import { Directive } from '../directives'
|
|
|
|
import { nextTick } from '../scheduler'
|
2021-04-05 22:13:29 +00:00
|
|
|
import { warnDeprecation, DeprecationTypes } from './deprecations'
|
|
|
|
import { version } from '..'
|
2021-04-06 14:02:46 +00:00
|
|
|
import { LegacyConfig } from './globalConfig'
|
2021-04-06 15:57:10 +00:00
|
|
|
import { LegacyDirective } from './customDirective'
|
2021-04-07 15:22:56 +00:00
|
|
|
import { configureCompat } from './compatConfig'
|
2021-04-05 21:09:22 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @deprecated the default `Vue` export has been removed in Vue 3. The type for
|
|
|
|
* the default export is provided only for migration purposes. Please use
|
|
|
|
* named imports instead - e.g. `import { createApp } from 'vue'`.
|
|
|
|
*/
|
|
|
|
export type CompatVue = Pick<App, 'version' | 'component' | 'directive'> & {
|
|
|
|
// no inference here since these types are not meant for actual use - they
|
|
|
|
// are merely here to provide type checks for internal implementation and
|
|
|
|
// information for migration.
|
|
|
|
new (options?: ComponentOptions): ComponentPublicInstance
|
|
|
|
|
|
|
|
version: string
|
2021-04-05 22:13:29 +00:00
|
|
|
config: AppConfig & LegacyConfig
|
2021-04-05 21:09:22 +00:00
|
|
|
|
|
|
|
extend: typeof defineComponent
|
|
|
|
nextTick: typeof nextTick
|
|
|
|
|
|
|
|
use(plugin: Plugin, ...options: any[]): CompatVue
|
|
|
|
mixin(mixin: ComponentOptions): CompatVue
|
|
|
|
|
|
|
|
component(name: string): Component | undefined
|
|
|
|
component(name: string, component: Component): CompatVue
|
|
|
|
directive(name: string): Directive | undefined
|
|
|
|
directive(name: string, directive: Directive): CompatVue
|
|
|
|
|
|
|
|
compile(template: string): RenderFunction
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @deprecated Vue 3 no longer needs set() for adding new properties.
|
|
|
|
*/
|
|
|
|
set(target: any, key: string | number | symbol, value: any): void
|
|
|
|
/**
|
|
|
|
* @deprecated Vue 3 no longer needs delete() for property deletions.
|
|
|
|
*/
|
|
|
|
delete(target: any, key: string | number | symbol): void
|
|
|
|
/**
|
|
|
|
* @deprecated use `reactive` instead.
|
|
|
|
*/
|
|
|
|
observable: typeof reactive
|
|
|
|
/**
|
|
|
|
* @deprecated filters have been removed from Vue 3.
|
|
|
|
*/
|
|
|
|
filter(name: string, arg: any): null
|
2021-04-07 15:22:56 +00:00
|
|
|
|
|
|
|
configureCompat: typeof configureCompat
|
2021-04-05 21:09:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
export let isCopyingConfig = false
|
|
|
|
|
|
|
|
// Legacy global Vue constructor
|
2021-04-05 22:13:29 +00:00
|
|
|
export function createCompatVue(
|
|
|
|
createApp: CreateAppFunction<Element>
|
|
|
|
): CompatVue {
|
2021-04-05 21:09:22 +00:00
|
|
|
const Vue: CompatVue = function Vue(options: ComponentOptions = {}) {
|
|
|
|
return createCompatApp(options, Vue)
|
|
|
|
} as any
|
|
|
|
|
|
|
|
const singletonApp = createApp({})
|
|
|
|
|
|
|
|
function createCompatApp(options: ComponentOptions = {}, Ctor: any) {
|
2021-04-06 19:02:07 +00:00
|
|
|
const { data } = options
|
|
|
|
if (data && !isFunction(data)) {
|
|
|
|
__DEV__ && warnDeprecation(DeprecationTypes.OPTIONS_DATA_FN)
|
|
|
|
options.data = () => data
|
|
|
|
}
|
|
|
|
|
2021-04-05 21:09:22 +00:00
|
|
|
const app = createApp(options)
|
|
|
|
|
2021-04-06 15:57:10 +00:00
|
|
|
// copy over asset registries and deopt flag
|
|
|
|
;['mixins', 'components', 'directives', 'deopt'].forEach(key => {
|
|
|
|
// @ts-ignore
|
|
|
|
app._context[key] = singletonApp._context[key]
|
|
|
|
})
|
|
|
|
|
2021-04-05 21:09:22 +00:00
|
|
|
// copy over global config mutations
|
|
|
|
isCopyingConfig = true
|
|
|
|
for (const key in singletonApp.config) {
|
|
|
|
if (
|
|
|
|
key !== 'isNativeTag' &&
|
|
|
|
!(key === 'isCustomElement' && isRuntimeOnly())
|
|
|
|
) {
|
|
|
|
// @ts-ignore
|
|
|
|
app.config[key] = singletonApp.config[key]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
isCopyingConfig = false
|
|
|
|
|
|
|
|
// copy prototype augmentations as config.globalProperties
|
2021-04-05 22:13:29 +00:00
|
|
|
let hasPrototypeAugmentations = false
|
2021-04-05 21:09:22 +00:00
|
|
|
for (const key in Ctor.prototype) {
|
2021-04-05 22:13:29 +00:00
|
|
|
if (key !== 'constructor') {
|
|
|
|
hasPrototypeAugmentations = true
|
|
|
|
}
|
2021-04-05 21:09:22 +00:00
|
|
|
app.config.globalProperties[key] = Ctor.prototype[key]
|
|
|
|
}
|
2021-04-05 22:13:29 +00:00
|
|
|
if (hasPrototypeAugmentations) {
|
|
|
|
__DEV__ && warnDeprecation(DeprecationTypes.GLOBAL_PROTOTYPE)
|
|
|
|
}
|
2021-04-05 21:09:22 +00:00
|
|
|
|
|
|
|
const vm = app._createRoot!(options)
|
|
|
|
if (options.el) {
|
|
|
|
return (vm as any).$mount(options.el)
|
|
|
|
} else {
|
|
|
|
return vm
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Vue.version = __VERSION__
|
|
|
|
Vue.config = singletonApp.config
|
|
|
|
|
|
|
|
Vue.extend = ((options: ComponentOptions = {}) => {
|
|
|
|
function SubVue(inlineOptions?: ComponentOptions) {
|
|
|
|
if (!inlineOptions) {
|
|
|
|
return createCompatApp(options, SubVue)
|
|
|
|
} else {
|
|
|
|
return createCompatApp(
|
|
|
|
{
|
2021-04-06 19:02:07 +00:00
|
|
|
el: inlineOptions.el,
|
2021-04-05 21:09:22 +00:00
|
|
|
extends: options,
|
|
|
|
mixins: [inlineOptions]
|
|
|
|
},
|
|
|
|
SubVue
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
SubVue.prototype = Object.create(Vue.prototype)
|
|
|
|
SubVue.prototype.constructor = SubVue
|
|
|
|
return SubVue
|
|
|
|
}) as any
|
|
|
|
|
|
|
|
Vue.nextTick = nextTick
|
|
|
|
|
|
|
|
Vue.set = (target, key, value) => {
|
2021-04-05 22:13:29 +00:00
|
|
|
__DEV__ && warnDeprecation(DeprecationTypes.GLOBAL_SET)
|
2021-04-05 21:09:22 +00:00
|
|
|
target[key] = value
|
|
|
|
}
|
2021-04-05 22:13:29 +00:00
|
|
|
|
2021-04-05 21:09:22 +00:00
|
|
|
Vue.delete = (target, key) => {
|
2021-04-05 22:13:29 +00:00
|
|
|
__DEV__ && warnDeprecation(DeprecationTypes.GLOBAL_DELETE)
|
2021-04-05 21:09:22 +00:00
|
|
|
delete target[key]
|
|
|
|
}
|
2021-04-05 22:13:29 +00:00
|
|
|
|
|
|
|
Vue.observable = __DEV__
|
|
|
|
? (target: any) => {
|
|
|
|
warnDeprecation(DeprecationTypes.GLOBAL_OBSERVABLE)
|
|
|
|
return reactive(target)
|
|
|
|
}
|
|
|
|
: reactive
|
2021-04-05 21:09:22 +00:00
|
|
|
|
|
|
|
Vue.use = (p, ...options) => {
|
|
|
|
singletonApp.use(p, ...options)
|
|
|
|
return Vue
|
|
|
|
}
|
|
|
|
|
|
|
|
Vue.mixin = m => {
|
|
|
|
singletonApp.mixin(m)
|
|
|
|
return Vue
|
|
|
|
}
|
|
|
|
|
2021-04-06 15:57:10 +00:00
|
|
|
Vue.component = ((name: string, comp: Component) => {
|
2021-04-05 21:09:22 +00:00
|
|
|
if (comp) {
|
|
|
|
singletonApp.component(name, comp)
|
|
|
|
return Vue
|
|
|
|
} else {
|
|
|
|
return singletonApp.component(name)
|
|
|
|
}
|
|
|
|
}) as any
|
|
|
|
|
2021-04-06 15:57:10 +00:00
|
|
|
Vue.directive = ((name: string, dir: Directive | LegacyDirective) => {
|
2021-04-05 21:09:22 +00:00
|
|
|
if (dir) {
|
2021-04-06 15:57:10 +00:00
|
|
|
singletonApp.directive(name, dir as Directive)
|
2021-04-05 21:09:22 +00:00
|
|
|
return Vue
|
|
|
|
} else {
|
|
|
|
return singletonApp.directive(name)
|
|
|
|
}
|
|
|
|
}) as any
|
|
|
|
|
|
|
|
Vue.filter = ((name: string, filter: any) => {
|
|
|
|
// TODO deprecation warning
|
|
|
|
// TODO compiler warning for filters (maybe behavior compat?)
|
|
|
|
}) as any
|
|
|
|
|
2021-04-07 15:22:56 +00:00
|
|
|
Vue.configureCompat = configureCompat
|
|
|
|
|
2021-04-05 21:09:22 +00:00
|
|
|
return Vue
|
|
|
|
}
|
2021-04-05 22:13:29 +00:00
|
|
|
|
|
|
|
export function installCompatMount(
|
|
|
|
app: App,
|
|
|
|
context: AppContext,
|
|
|
|
render: RootRenderFunction,
|
|
|
|
hydrate?: RootHydrateFunction
|
|
|
|
) {
|
|
|
|
let isMounted = false
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Vue 2 supports the behavior of creating a component instance but not
|
|
|
|
* mounting it, which is no longer possible in Vue 3 - this internal
|
|
|
|
* function simulates that behavior.
|
|
|
|
*/
|
|
|
|
app._createRoot = options => {
|
|
|
|
const component = app._component
|
|
|
|
const vnode = createVNode(component, options.propsData || null)
|
|
|
|
vnode.appContext = context
|
|
|
|
|
|
|
|
const hasNoRender =
|
|
|
|
!isFunction(component) && !component.render && !component.template
|
|
|
|
const emptyRender = () => {}
|
|
|
|
|
|
|
|
// create root instance
|
|
|
|
const instance = createComponentInstance(vnode, null, null)
|
|
|
|
// suppress "missing render fn" warning since it can't be determined
|
|
|
|
// until $mount is called
|
|
|
|
if (hasNoRender) {
|
|
|
|
instance.render = emptyRender
|
|
|
|
}
|
2021-04-06 16:17:17 +00:00
|
|
|
setupComponent(instance)
|
2021-04-05 22:13:29 +00:00
|
|
|
vnode.component = instance
|
|
|
|
|
|
|
|
// $mount & $destroy
|
|
|
|
// these are defined on ctx and picked up by the $mount/$destroy
|
|
|
|
// public property getters on the instance proxy.
|
|
|
|
// Note: the following assumes DOM environment since the compat build
|
|
|
|
// only targets web. It essentially includes logic for app.mount from
|
|
|
|
// both runtime-core AND runtime-dom.
|
|
|
|
instance.ctx._compat_mount = (selectorOrEl: string | Element) => {
|
|
|
|
if (isMounted) {
|
|
|
|
__DEV__ && warn(`Root instance is already mounted.`)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
let container: Element
|
|
|
|
if (typeof selectorOrEl === 'string') {
|
|
|
|
// eslint-disable-next-line
|
|
|
|
const result = document.querySelector(selectorOrEl)
|
|
|
|
if (!result) {
|
|
|
|
__DEV__ &&
|
|
|
|
warn(
|
|
|
|
`Failed to mount root instance: selector "${selectorOrEl}" returned null.`
|
|
|
|
)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
container = result
|
|
|
|
} else {
|
|
|
|
if (!selectorOrEl) {
|
|
|
|
__DEV__ &&
|
|
|
|
warn(
|
|
|
|
`Failed to mount root instance: invalid mount target ${selectorOrEl}.`
|
|
|
|
)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
container = selectorOrEl
|
|
|
|
}
|
|
|
|
|
|
|
|
const isSVG = container instanceof SVGElement
|
|
|
|
|
|
|
|
// HMR root reload
|
|
|
|
if (__DEV__) {
|
|
|
|
context.reload = () => {
|
|
|
|
const cloned = cloneVNode(vnode)
|
|
|
|
// compat mode will use instance if not reset to null
|
|
|
|
cloned.component = null
|
|
|
|
render(cloned, container, isSVG)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// resolve in-DOM template if component did not provide render
|
|
|
|
// and no setup/mixin render functions are provided (by checking
|
|
|
|
// that the instance is still using the placeholder render fn)
|
|
|
|
if (hasNoRender && instance.render === emptyRender) {
|
|
|
|
// root directives check
|
|
|
|
if (__DEV__) {
|
|
|
|
for (let i = 0; i < container.attributes.length; i++) {
|
|
|
|
const attr = container.attributes[i]
|
|
|
|
if (attr.name !== 'v-cloak' && /^(v-|:|@)/.test(attr.name)) {
|
2021-04-07 15:22:56 +00:00
|
|
|
warnDeprecation(DeprecationTypes.GLOBAL_MOUNT_CONTAINER)
|
2021-04-05 22:13:29 +00:00
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
instance.render = null
|
|
|
|
;(component as ComponentOptions).template = container.innerHTML
|
2021-04-06 16:17:17 +00:00
|
|
|
finishComponentSetup(instance, false, true /* skip options */)
|
2021-04-05 22:13:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// clear content before mounting
|
|
|
|
container.innerHTML = ''
|
|
|
|
|
|
|
|
// TODO hydration
|
|
|
|
render(vnode, container, isSVG)
|
|
|
|
|
|
|
|
if (container instanceof Element) {
|
|
|
|
container.removeAttribute('v-cloak')
|
|
|
|
container.setAttribute('data-v-app', '')
|
|
|
|
}
|
|
|
|
|
|
|
|
isMounted = true
|
|
|
|
app._container = container
|
|
|
|
// for devtools and telemetry
|
|
|
|
;(container as any).__vue_app__ = app
|
|
|
|
if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
|
|
|
|
devtoolsInitApp(app, version)
|
|
|
|
}
|
|
|
|
|
|
|
|
return instance.proxy!
|
|
|
|
}
|
|
|
|
|
|
|
|
instance.ctx._compat_destroy = app.unmount
|
|
|
|
|
|
|
|
return instance.proxy!
|
|
|
|
}
|
|
|
|
}
|