wip: restructure compat code + global api alignment
This commit is contained in:
parent
e2d6ff845b
commit
40e3dd28e1
@ -12,7 +12,8 @@ module.exports = {
|
|||||||
__NODE_JS__: true,
|
__NODE_JS__: true,
|
||||||
__FEATURE_OPTIONS_API__: true,
|
__FEATURE_OPTIONS_API__: true,
|
||||||
__FEATURE_SUSPENSE__: true,
|
__FEATURE_SUSPENSE__: true,
|
||||||
__FEATURE_PROD_DEVTOOLS__: false
|
__FEATURE_PROD_DEVTOOLS__: false,
|
||||||
|
__COMPAT__: true
|
||||||
},
|
},
|
||||||
coverageDirectory: 'coverage',
|
coverageDirectory: 'coverage',
|
||||||
coverageReporters: ['html', 'lcov', 'text'],
|
coverageReporters: ['html', 'lcov', 'text'],
|
||||||
|
@ -2,10 +2,7 @@ import {
|
|||||||
ConcreteComponent,
|
ConcreteComponent,
|
||||||
Data,
|
Data,
|
||||||
validateComponentName,
|
validateComponentName,
|
||||||
Component,
|
Component
|
||||||
createComponentInstance,
|
|
||||||
setupComponent,
|
|
||||||
finishComponentSetup
|
|
||||||
} from './component'
|
} from './component'
|
||||||
import { ComponentOptions } from './componentOptions'
|
import { ComponentOptions } from './componentOptions'
|
||||||
import { ComponentPublicInstance } from './componentPublicInstance'
|
import { ComponentPublicInstance } from './componentPublicInstance'
|
||||||
@ -16,14 +13,10 @@ import { warn } from './warning'
|
|||||||
import { createVNode, cloneVNode, VNode } from './vnode'
|
import { createVNode, cloneVNode, VNode } from './vnode'
|
||||||
import { RootHydrateFunction } from './hydration'
|
import { RootHydrateFunction } from './hydration'
|
||||||
import { devtoolsInitApp, devtoolsUnmountApp } from './devtools'
|
import { devtoolsInitApp, devtoolsUnmountApp } from './devtools'
|
||||||
|
import { isFunction, NO, isObject } from '@vue/shared'
|
||||||
import { version } from '.'
|
import { version } from '.'
|
||||||
import {
|
import { installCompatMount } from './compat/globalMount'
|
||||||
isFunction,
|
import { installLegacyConfigTraps } from './compat/globalConfig'
|
||||||
NO,
|
|
||||||
isObject,
|
|
||||||
warnDeprecation,
|
|
||||||
DeprecationTypes
|
|
||||||
} from '@vue/shared'
|
|
||||||
|
|
||||||
export interface App<HostElement = any> {
|
export interface App<HostElement = any> {
|
||||||
version: string
|
version: string
|
||||||
@ -313,126 +306,8 @@ export function createAppAPI<HostElement>(
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (__COMPAT__) {
|
if (__COMPAT__) {
|
||||||
/**
|
installCompatMount(app, context, render, hydrate)
|
||||||
* Vue 2 supports the behavior of creating a component instance but not
|
installLegacyConfigTraps(app.config)
|
||||||
* mounting it, which is no longer possible in Vue 3 - this internal
|
|
||||||
* function simulates that behavior.
|
|
||||||
*/
|
|
||||||
app._createRoot = options => {
|
|
||||||
const vnode = createVNode(
|
|
||||||
rootComponent as ConcreteComponent,
|
|
||||||
options.propsData || null
|
|
||||||
)
|
|
||||||
vnode.appContext = context
|
|
||||||
|
|
||||||
const hasNoRender =
|
|
||||||
!isFunction(rootComponent) &&
|
|
||||||
!rootComponent.render &&
|
|
||||||
!rootComponent.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
|
|
||||||
}
|
|
||||||
setupComponent(instance, __NODE_JS__)
|
|
||||||
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)) {
|
|
||||||
warnDeprecation(DeprecationTypes.DOM_TEMPLATE_MOUNT)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
instance.render = null
|
|
||||||
;(rootComponent as ComponentOptions).template = container.innerHTML
|
|
||||||
finishComponentSetup(instance, __NODE_JS__, true /* skip options */)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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!
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return app
|
return app
|
||||||
|
91
packages/runtime-core/src/compat/deprecations.ts
Normal file
91
packages/runtime-core/src/compat/deprecations.ts
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
import { isRuntimeOnly } from '../component'
|
||||||
|
|
||||||
|
export const enum DeprecationTypes {
|
||||||
|
DOM_TEMPLATE_MOUNT,
|
||||||
|
$MOUNT,
|
||||||
|
$DESTROY,
|
||||||
|
|
||||||
|
CONFIG_SILENT,
|
||||||
|
CONFIG_DEVTOOLS,
|
||||||
|
CONFIG_KEY_CODES,
|
||||||
|
CONFIG_PRODUCTION_TIP,
|
||||||
|
CONFIG_IGNORED_ELEMENTS
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeprecationData = {
|
||||||
|
message: string | (() => string)
|
||||||
|
link?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const deprecations: Record<DeprecationTypes, DeprecationData> = {
|
||||||
|
[DeprecationTypes.DOM_TEMPLATE_MOUNT]: {
|
||||||
|
message:
|
||||||
|
`Vue detected directives on the mount container. ` +
|
||||||
|
`In Vue 3, the container is no longer considered part of the template ` +
|
||||||
|
`and will not be processed/replaced.`,
|
||||||
|
link: `https://v3.vuejs.org/guide/migration/mount-changes.html`
|
||||||
|
},
|
||||||
|
|
||||||
|
[DeprecationTypes.$MOUNT]: {
|
||||||
|
message:
|
||||||
|
`vm.$mount() has been removed. ` +
|
||||||
|
`Use createApp(RootComponent).mount() instead.`,
|
||||||
|
link: `https://v3.vuejs.org/guide/migration/global-api.html#mounting-app-instance`
|
||||||
|
},
|
||||||
|
|
||||||
|
[DeprecationTypes.$DESTROY]: {
|
||||||
|
message: `vm.$destroy() has been removed. Use app.unmount() instead.`,
|
||||||
|
link: `https://v3.vuejs.org/api/application-api.html#unmount`
|
||||||
|
},
|
||||||
|
|
||||||
|
[DeprecationTypes.CONFIG_SILENT]: {
|
||||||
|
message:
|
||||||
|
`config.silent has been removed because it is not good practice to ` +
|
||||||
|
`intentionally suppress warnings. You can use your browser console's ` +
|
||||||
|
`filter features to focus on relevant messages.`
|
||||||
|
},
|
||||||
|
|
||||||
|
[DeprecationTypes.CONFIG_DEVTOOLS]: {
|
||||||
|
message:
|
||||||
|
`config.devtools has been removed. To enable devtools for ` +
|
||||||
|
`production, configure the __VUE_PROD_DEVTOOLS__ compile-time flag.`,
|
||||||
|
link: `https://github.com/vuejs/vue-next/tree/master/packages/vue#bundler-build-feature-flags`
|
||||||
|
},
|
||||||
|
|
||||||
|
[DeprecationTypes.CONFIG_KEY_CODES]: {
|
||||||
|
message:
|
||||||
|
`config.keyCodes has been removed. ` +
|
||||||
|
`In Vue 3, you can directly use the kebab-case key names as v-on modifiers.`,
|
||||||
|
link: `https://v3.vuejs.org/guide/migration/keycode-modifiers.html`
|
||||||
|
},
|
||||||
|
|
||||||
|
[DeprecationTypes.CONFIG_PRODUCTION_TIP]: {
|
||||||
|
message: `config.productionTip has been removed.`,
|
||||||
|
link: `https://v3.vuejs.org/guide/migration/global-api.html#config-productiontip-removed`
|
||||||
|
},
|
||||||
|
|
||||||
|
[DeprecationTypes.CONFIG_IGNORED_ELEMENTS]: {
|
||||||
|
message: () => {
|
||||||
|
let msg = `config.ignoredElements has been removed.`
|
||||||
|
if (isRuntimeOnly()) {
|
||||||
|
msg += ` Pass the "isCustomElement" option to @vue/compiler-dom instead.`
|
||||||
|
} else {
|
||||||
|
msg += ` Use config.isCustomElement instead.`
|
||||||
|
}
|
||||||
|
return msg
|
||||||
|
},
|
||||||
|
link: `https://v3.vuejs.org/guide/migration/global-api.html#config-ignoredelements-is-now-config-iscustomelement`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function warnDeprecation(key: DeprecationTypes) {
|
||||||
|
if (!__COMPAT__ || !__DEV__) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const { message, link } = deprecations[key]
|
||||||
|
console.warn(
|
||||||
|
`[Vue Deprecation]: ${typeof message === 'function' ? message() : message}${
|
||||||
|
link ? `\nFor more details, see ${link}` : ``
|
||||||
|
}`
|
||||||
|
)
|
||||||
|
}
|
171
packages/runtime-core/src/compat/global.ts
Normal file
171
packages/runtime-core/src/compat/global.ts
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
import { reactive } from '@vue/reactivity'
|
||||||
|
import { extend } from '@vue/shared'
|
||||||
|
import { createApp } from '../../../runtime-dom/src'
|
||||||
|
import { App, AppConfig, Plugin } from '../apiCreateApp'
|
||||||
|
import { defineComponent } from '../apiDefineComponent'
|
||||||
|
import { Component, ComponentOptions, isRuntimeOnly } from '../component'
|
||||||
|
import { RenderFunction } from '../componentOptions'
|
||||||
|
import { ComponentPublicInstance } from '../componentPublicInstance'
|
||||||
|
import { Directive } from '../directives'
|
||||||
|
import { nextTick } from '../scheduler'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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
|
||||||
|
config: AppConfig
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
export let isCopyingConfig = false
|
||||||
|
|
||||||
|
// Legacy global Vue constructor
|
||||||
|
export function createCompatVue(): CompatVue {
|
||||||
|
if (!__COMPAT__) {
|
||||||
|
// @ts-ignore this function will never be called in non-compat mode
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const Vue: CompatVue = function Vue(options: ComponentOptions = {}) {
|
||||||
|
return createCompatApp(options, Vue)
|
||||||
|
} as any
|
||||||
|
|
||||||
|
const singletonApp = createApp({})
|
||||||
|
|
||||||
|
function createCompatApp(options: ComponentOptions = {}, Ctor: any) {
|
||||||
|
const app = createApp(options)
|
||||||
|
|
||||||
|
// 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
|
||||||
|
for (const key in Ctor.prototype) {
|
||||||
|
app.config.globalProperties[key] = Ctor.prototype[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
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(
|
||||||
|
{
|
||||||
|
el: inlineOptions.el,
|
||||||
|
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) => {
|
||||||
|
// TODO deprecation warnings
|
||||||
|
target[key] = value
|
||||||
|
}
|
||||||
|
Vue.delete = (target, key) => {
|
||||||
|
// TODO deprecation warnings
|
||||||
|
delete target[key]
|
||||||
|
}
|
||||||
|
// TODO wrap with deprecation warning
|
||||||
|
Vue.observable = reactive
|
||||||
|
|
||||||
|
Vue.use = (p, ...options) => {
|
||||||
|
singletonApp.use(p, ...options)
|
||||||
|
return Vue
|
||||||
|
}
|
||||||
|
|
||||||
|
Vue.mixin = m => {
|
||||||
|
singletonApp.mixin(m)
|
||||||
|
return Vue
|
||||||
|
}
|
||||||
|
|
||||||
|
Vue.component = ((name: string, comp: any) => {
|
||||||
|
if (comp) {
|
||||||
|
singletonApp.component(name, comp)
|
||||||
|
return Vue
|
||||||
|
} else {
|
||||||
|
return singletonApp.component(name)
|
||||||
|
}
|
||||||
|
}) as any
|
||||||
|
|
||||||
|
Vue.directive = ((name: string, dir: any) => {
|
||||||
|
if (dir) {
|
||||||
|
singletonApp.directive(name, dir)
|
||||||
|
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
|
||||||
|
|
||||||
|
return Vue
|
||||||
|
}
|
57
packages/runtime-core/src/compat/globalConfig.ts
Normal file
57
packages/runtime-core/src/compat/globalConfig.ts
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import { AppConfig } from '../apiCreateApp'
|
||||||
|
import { DeprecationTypes, warnDeprecation } from './deprecations'
|
||||||
|
import { isCopyingConfig } from './global'
|
||||||
|
|
||||||
|
// legacy config warnings
|
||||||
|
export type LegacyConfig = {
|
||||||
|
/**
|
||||||
|
* @deprecated `config.silent` option has been removed
|
||||||
|
*/
|
||||||
|
silent?: boolean
|
||||||
|
/**
|
||||||
|
* @deprecated use __VUE_PROD_DEVTOOLS__ compile-time feature flag instead
|
||||||
|
* https://github.com/vuejs/vue-next/tree/master/packages/vue#bundler-build-feature-flags
|
||||||
|
*/
|
||||||
|
devtools?: boolean
|
||||||
|
/**
|
||||||
|
* @deprecated use `config.isCustomElement` instead
|
||||||
|
* https://v3.vuejs.org/guide/migration/global-api.html#config-ignoredelements-is-now-config-iscustomelement
|
||||||
|
*/
|
||||||
|
ignoredElements?: (string | RegExp)[]
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
* https://v3.vuejs.org/guide/migration/keycode-modifiers.html
|
||||||
|
*/
|
||||||
|
keyCodes?: Record<string, number | number[]>
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
* https://v3.vuejs.org/guide/migration/global-api.html#config-productiontip-removed
|
||||||
|
*/
|
||||||
|
productionTip?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export function installLegacyConfigTraps(config: AppConfig) {
|
||||||
|
const legacyConfigOptions: Record<string, DeprecationTypes> = {
|
||||||
|
silent: DeprecationTypes.CONFIG_SILENT,
|
||||||
|
devtools: DeprecationTypes.CONFIG_DEVTOOLS,
|
||||||
|
ignoredElements: DeprecationTypes.CONFIG_IGNORED_ELEMENTS,
|
||||||
|
keyCodes: DeprecationTypes.CONFIG_KEY_CODES,
|
||||||
|
productionTip: DeprecationTypes.CONFIG_PRODUCTION_TIP
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.keys(legacyConfigOptions).forEach(key => {
|
||||||
|
let val = (config as any)[key]
|
||||||
|
Object.defineProperty(config, key, {
|
||||||
|
enumerable: true,
|
||||||
|
get() {
|
||||||
|
return val
|
||||||
|
},
|
||||||
|
set(newVal) {
|
||||||
|
if (!isCopyingConfig) {
|
||||||
|
warnDeprecation(legacyConfigOptions[key])
|
||||||
|
}
|
||||||
|
val = newVal
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
141
packages/runtime-core/src/compat/globalMount.ts
Normal file
141
packages/runtime-core/src/compat/globalMount.ts
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
import { isFunction } from '@vue/shared'
|
||||||
|
import { App, AppContext } from '../apiCreateApp'
|
||||||
|
import {
|
||||||
|
ComponentOptions,
|
||||||
|
createComponentInstance,
|
||||||
|
finishComponentSetup,
|
||||||
|
setupComponent
|
||||||
|
} from '../component'
|
||||||
|
import { devtoolsInitApp } from '../devtools'
|
||||||
|
import { RootHydrateFunction } from '../hydration'
|
||||||
|
import { RootRenderFunction } from '../renderer'
|
||||||
|
import { cloneVNode, createVNode } from '../vnode'
|
||||||
|
import { warn } from '../warning'
|
||||||
|
import { version } from '..'
|
||||||
|
import { DeprecationTypes, warnDeprecation } from './deprecations'
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
setupComponent(instance, __NODE_JS__)
|
||||||
|
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)) {
|
||||||
|
warnDeprecation(DeprecationTypes.DOM_TEMPLATE_MOUNT)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
instance.render = null
|
||||||
|
;(component as ComponentOptions).template = container.innerHTML
|
||||||
|
finishComponentSetup(instance, __NODE_JS__, true /* skip options */)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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!
|
||||||
|
}
|
||||||
|
}
|
@ -12,8 +12,7 @@ import {
|
|||||||
NOOP,
|
NOOP,
|
||||||
extend,
|
extend,
|
||||||
isString,
|
isString,
|
||||||
warnDeprecation,
|
isFunction
|
||||||
DeprecationTypes
|
|
||||||
} from '@vue/shared'
|
} from '@vue/shared'
|
||||||
import {
|
import {
|
||||||
ReactiveEffect,
|
ReactiveEffect,
|
||||||
@ -42,7 +41,7 @@ import { markAttrsAccessed } from './componentRenderUtils'
|
|||||||
import { currentRenderingInstance } from './componentRenderContext'
|
import { currentRenderingInstance } from './componentRenderContext'
|
||||||
import { warn } from './warning'
|
import { warn } from './warning'
|
||||||
import { UnionToIntersection } from './helpers/typeUtils'
|
import { UnionToIntersection } from './helpers/typeUtils'
|
||||||
|
import { warnDeprecation, DeprecationTypes } from './compat/deprecations'
|
||||||
/**
|
/**
|
||||||
* Custom properties added to component instances in any way and can be accessed through `this`
|
* Custom properties added to component instances in any way and can be accessed through `this`
|
||||||
*
|
*
|
||||||
@ -356,7 +355,12 @@ export const PublicInstanceProxyHandlers: ProxyHandler<any> = {
|
|||||||
((globalProperties = appContext.config.globalProperties),
|
((globalProperties = appContext.config.globalProperties),
|
||||||
hasOwn(globalProperties, key))
|
hasOwn(globalProperties, key))
|
||||||
) {
|
) {
|
||||||
return globalProperties[key]
|
if (__COMPAT__) {
|
||||||
|
const val = globalProperties[key]
|
||||||
|
return isFunction(val) ? val.bind(instance.proxy) : val
|
||||||
|
} else {
|
||||||
|
return globalProperties[key]
|
||||||
|
}
|
||||||
} else if (
|
} else if (
|
||||||
__DEV__ &&
|
__DEV__ &&
|
||||||
currentRenderingInstance &&
|
currentRenderingInstance &&
|
||||||
@ -510,7 +514,10 @@ export function createRenderContext(instance: ComponentInternalInstance) {
|
|||||||
Object.defineProperty(target, key, {
|
Object.defineProperty(target, key, {
|
||||||
configurable: true,
|
configurable: true,
|
||||||
enumerable: false,
|
enumerable: false,
|
||||||
get: () => globalProperties[key],
|
get: () => {
|
||||||
|
const val = globalProperties[key]
|
||||||
|
return __COMPAT__ && isFunction(val) ? val.bind(instance.proxy) : val
|
||||||
|
},
|
||||||
set: NOOP
|
set: NOOP
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -279,3 +279,10 @@ const _ssrUtils = {
|
|||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
export const ssrUtils = (__NODE_JS__ ? _ssrUtils : null) as typeof _ssrUtils
|
export const ssrUtils = (__NODE_JS__ ? _ssrUtils : null) as typeof _ssrUtils
|
||||||
|
|
||||||
|
// 2.x COMPAT ------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Important: every function exported here must have `if (!__COMPAT__) return`
|
||||||
|
// checks
|
||||||
|
export { warnDeprecation, DeprecationTypes } from './compat/deprecations'
|
||||||
|
export { createCompatVue, CompatVue } from './compat/global'
|
||||||
|
@ -8,20 +8,14 @@ import {
|
|||||||
HydrationRenderer,
|
HydrationRenderer,
|
||||||
App,
|
App,
|
||||||
RootHydrateFunction,
|
RootHydrateFunction,
|
||||||
isRuntimeOnly
|
isRuntimeOnly,
|
||||||
|
warnDeprecation,
|
||||||
|
DeprecationTypes
|
||||||
} from '@vue/runtime-core'
|
} from '@vue/runtime-core'
|
||||||
import { nodeOps } from './nodeOps'
|
import { nodeOps } from './nodeOps'
|
||||||
import { patchProp, forcePatchProp } from './patchProp'
|
import { patchProp, forcePatchProp } from './patchProp'
|
||||||
// Importing from the compiler, will be tree-shaken in prod
|
// Importing from the compiler, will be tree-shaken in prod
|
||||||
import {
|
import { isFunction, isString, isHTMLTag, isSVGTag, extend } from '@vue/shared'
|
||||||
isFunction,
|
|
||||||
isString,
|
|
||||||
isHTMLTag,
|
|
||||||
isSVGTag,
|
|
||||||
extend,
|
|
||||||
warnDeprecation,
|
|
||||||
DeprecationTypes
|
|
||||||
} from '@vue/shared'
|
|
||||||
|
|
||||||
declare module '@vue/reactivity' {
|
declare module '@vue/reactivity' {
|
||||||
export interface RefUnwrapBailTypes {
|
export interface RefUnwrapBailTypes {
|
||||||
|
@ -1,39 +0,0 @@
|
|||||||
export const enum DeprecationTypes {
|
|
||||||
DOM_TEMPLATE_MOUNT,
|
|
||||||
$MOUNT,
|
|
||||||
$DESTROY
|
|
||||||
}
|
|
||||||
|
|
||||||
type DeprecationData = {
|
|
||||||
message: string
|
|
||||||
link?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
const deprecations: Record<DeprecationTypes, DeprecationData> = {
|
|
||||||
[DeprecationTypes.DOM_TEMPLATE_MOUNT]: {
|
|
||||||
message:
|
|
||||||
`Vue detected directives on the mount container. ` +
|
|
||||||
`In Vue 3, the container is no longer considered part of the template ` +
|
|
||||||
`and will not be processed/replaced.`,
|
|
||||||
link: `https://v3.vuejs.org/guide/migration/mount-changes.html`
|
|
||||||
},
|
|
||||||
|
|
||||||
[DeprecationTypes.$MOUNT]: {
|
|
||||||
message:
|
|
||||||
`vm.$mount() has been deprecated. ` +
|
|
||||||
`Use createApp(RootComponent).mount() instead.`,
|
|
||||||
link: `https://v3.vuejs.org/guide/migration/global-api.html#mounting-app-instance`
|
|
||||||
},
|
|
||||||
|
|
||||||
[DeprecationTypes.$DESTROY]: {
|
|
||||||
message: `vm.$destroy() has been deprecated. Use app.unmount() instead.`,
|
|
||||||
link: `https://v3.vuejs.org/api/application-api.html#unmount`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function warnDeprecation(key: DeprecationTypes) {
|
|
||||||
const { message, link } = deprecations[key]
|
|
||||||
console.warn(
|
|
||||||
`[Deprecation]: ${message}${link ? `\nFor more details, see ${link}` : ``}`
|
|
||||||
)
|
|
||||||
}
|
|
@ -12,7 +12,6 @@ export * from './domAttrConfig'
|
|||||||
export * from './escapeHtml'
|
export * from './escapeHtml'
|
||||||
export * from './looseEqual'
|
export * from './looseEqual'
|
||||||
export * from './toDisplayString'
|
export * from './toDisplayString'
|
||||||
export * from './deprecations'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of @babel/parser plugins that are used for template expression
|
* List of @babel/parser plugins that are used for template expression
|
||||||
|
@ -1,175 +0,0 @@
|
|||||||
import { reactive } from '@vue/reactivity'
|
|
||||||
import {
|
|
||||||
createApp,
|
|
||||||
defineComponent,
|
|
||||||
nextTick,
|
|
||||||
App,
|
|
||||||
AppConfig,
|
|
||||||
Plugin,
|
|
||||||
Component,
|
|
||||||
ComponentOptions,
|
|
||||||
ComponentPublicInstance,
|
|
||||||
Directive,
|
|
||||||
RenderFunction,
|
|
||||||
isRuntimeOnly
|
|
||||||
} from '@vue/runtime-dom'
|
|
||||||
import { extend } from '@vue/shared'
|
|
||||||
|
|
||||||
// TODO make these getter/setters and trigger deprecation warnings
|
|
||||||
export type LegacyConfig = AppConfig & {
|
|
||||||
/**
|
|
||||||
* @deprecated `config.silent` option has been removed
|
|
||||||
*/
|
|
||||||
silent?: boolean
|
|
||||||
/**
|
|
||||||
* @deprecated use __VUE_PROD_DEVTOOLS__ compile-time feature flag instead
|
|
||||||
* https://github.com/vuejs/vue-next/tree/master/packages/vue#bundler-build-feature-flags
|
|
||||||
*/
|
|
||||||
devtools?: boolean
|
|
||||||
/**
|
|
||||||
* @deprecated use `config.isCustomElement` instead
|
|
||||||
* https://v3.vuejs.org/guide/migration/global-api.html#config-ignoredelements-is-now-config-iscustomelement
|
|
||||||
*/
|
|
||||||
ignoredElements?: (string | RegExp)[]
|
|
||||||
/**
|
|
||||||
* @deprecated
|
|
||||||
* https://v3.vuejs.org/guide/migration/keycode-modifiers.html
|
|
||||||
*/
|
|
||||||
keyCodes?: Record<string, number | number[]>
|
|
||||||
/**
|
|
||||||
* @deprecated
|
|
||||||
* https://v3.vuejs.org/guide/migration/global-api.html#config-productiontip-removed
|
|
||||||
*/
|
|
||||||
productionTip?: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @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 GlobalVue = 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
|
|
||||||
config: LegacyConfig
|
|
||||||
|
|
||||||
extend: typeof defineComponent
|
|
||||||
nextTick: typeof nextTick
|
|
||||||
|
|
||||||
use(plugin: Plugin, ...options: any[]): GlobalVue
|
|
||||||
mixin(mixin: ComponentOptions): GlobalVue
|
|
||||||
|
|
||||||
component(name: string): Component | undefined
|
|
||||||
component(name: string, component: Component): GlobalVue
|
|
||||||
directive(name: string): Directive | undefined
|
|
||||||
directive(name: string, directive: Directive): GlobalVue
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
export const Vue: GlobalVue = function Vue(options: ComponentOptions = {}) {
|
|
||||||
const app = createApp(options)
|
|
||||||
|
|
||||||
// copy over global config mutations
|
|
||||||
for (const key in singletonApp.config) {
|
|
||||||
if (
|
|
||||||
key !== 'isNativeTag' &&
|
|
||||||
!(key === 'isCustomElement' && isRuntimeOnly())
|
|
||||||
) {
|
|
||||||
// @ts-ignore
|
|
||||||
app.config[key] = singletonApp.config[key]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO copy prototype augmentations as config.globalProperties
|
|
||||||
|
|
||||||
if (options.el) {
|
|
||||||
return app.mount(options.el)
|
|
||||||
} else {
|
|
||||||
return app._createRoot!(options)
|
|
||||||
}
|
|
||||||
} as any
|
|
||||||
|
|
||||||
const singletonApp = createApp({})
|
|
||||||
|
|
||||||
Vue.version = __VERSION__
|
|
||||||
Vue.config = singletonApp.config
|
|
||||||
|
|
||||||
Vue.extend = ((baseOptions: ComponentOptions = {}) => {
|
|
||||||
return function ExtendedVueConstructor(inlineOptions?: ComponentOptions) {
|
|
||||||
if (!inlineOptions) {
|
|
||||||
return new Vue(baseOptions)
|
|
||||||
} else {
|
|
||||||
const mergedOptions = extend({}, baseOptions)
|
|
||||||
mergedOptions.mixins = [inlineOptions, ...(mergedOptions.mixins || [])]
|
|
||||||
return new Vue(mergedOptions)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}) as any
|
|
||||||
|
|
||||||
Vue.nextTick = nextTick
|
|
||||||
|
|
||||||
Vue.set = (target, key, value) => {
|
|
||||||
// TODO deprecation warnings
|
|
||||||
target[key] = value
|
|
||||||
}
|
|
||||||
Vue.delete = (target, key) => {
|
|
||||||
// TODO deprecation warnings
|
|
||||||
delete target[key]
|
|
||||||
}
|
|
||||||
// TODO wrap with deprecation warning
|
|
||||||
Vue.observable = reactive
|
|
||||||
|
|
||||||
Vue.use = (p, ...options) => {
|
|
||||||
singletonApp.use(p, ...options)
|
|
||||||
return Vue
|
|
||||||
}
|
|
||||||
|
|
||||||
Vue.mixin = m => {
|
|
||||||
singletonApp.mixin(m)
|
|
||||||
return Vue
|
|
||||||
}
|
|
||||||
|
|
||||||
Vue.component = ((name: string, comp: any) => {
|
|
||||||
if (comp) {
|
|
||||||
singletonApp.component(name, comp)
|
|
||||||
return Vue
|
|
||||||
} else {
|
|
||||||
return singletonApp.component(name)
|
|
||||||
}
|
|
||||||
}) as any
|
|
||||||
|
|
||||||
Vue.directive = ((name: string, dir: any) => {
|
|
||||||
if (dir) {
|
|
||||||
singletonApp.directive(name, dir)
|
|
||||||
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
|
|
@ -2,11 +2,15 @@
|
|||||||
// and the compiler, and supports on-the-fly compilation of the template option.
|
// and the compiler, and supports on-the-fly compilation of the template option.
|
||||||
import { initDev } from './dev'
|
import { initDev } from './dev'
|
||||||
import { compile, CompilerOptions, CompilerError } from '@vue/compiler-dom'
|
import { compile, CompilerOptions, CompilerError } from '@vue/compiler-dom'
|
||||||
import { registerRuntimeCompiler, RenderFunction, warn } from '@vue/runtime-dom'
|
import {
|
||||||
|
registerRuntimeCompiler,
|
||||||
|
RenderFunction,
|
||||||
|
warn,
|
||||||
|
createCompatVue
|
||||||
|
} from '@vue/runtime-dom'
|
||||||
import { isString, NOOP, generateCodeFrame, extend } from '@vue/shared'
|
import { isString, NOOP, generateCodeFrame, extend } from '@vue/shared'
|
||||||
import { InternalRenderFunction } from 'packages/runtime-core/src/component'
|
import { InternalRenderFunction } from 'packages/runtime-core/src/component'
|
||||||
import * as runtimeDom from '@vue/runtime-dom'
|
import * as runtimeDom from '@vue/runtime-dom'
|
||||||
import { Vue } from './apiGlobal'
|
|
||||||
|
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
initDev()
|
initDev()
|
||||||
@ -87,6 +91,8 @@ function compileToFunction(
|
|||||||
|
|
||||||
registerRuntimeCompiler(compileToFunction)
|
registerRuntimeCompiler(compileToFunction)
|
||||||
|
|
||||||
|
const Vue = createCompatVue()
|
||||||
|
|
||||||
Vue.compile = compileToFunction
|
Vue.compile = compileToFunction
|
||||||
extend(Vue, runtimeDom)
|
extend(Vue, runtimeDom)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user