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,
|
||||
__FEATURE_OPTIONS_API__: true,
|
||||
__FEATURE_SUSPENSE__: true,
|
||||
__FEATURE_PROD_DEVTOOLS__: false
|
||||
__FEATURE_PROD_DEVTOOLS__: false,
|
||||
__COMPAT__: true
|
||||
},
|
||||
coverageDirectory: 'coverage',
|
||||
coverageReporters: ['html', 'lcov', 'text'],
|
||||
|
@ -2,10 +2,7 @@ import {
|
||||
ConcreteComponent,
|
||||
Data,
|
||||
validateComponentName,
|
||||
Component,
|
||||
createComponentInstance,
|
||||
setupComponent,
|
||||
finishComponentSetup
|
||||
Component
|
||||
} from './component'
|
||||
import { ComponentOptions } from './componentOptions'
|
||||
import { ComponentPublicInstance } from './componentPublicInstance'
|
||||
@ -16,14 +13,10 @@ import { warn } from './warning'
|
||||
import { createVNode, cloneVNode, VNode } from './vnode'
|
||||
import { RootHydrateFunction } from './hydration'
|
||||
import { devtoolsInitApp, devtoolsUnmountApp } from './devtools'
|
||||
import { isFunction, NO, isObject } from '@vue/shared'
|
||||
import { version } from '.'
|
||||
import {
|
||||
isFunction,
|
||||
NO,
|
||||
isObject,
|
||||
warnDeprecation,
|
||||
DeprecationTypes
|
||||
} from '@vue/shared'
|
||||
import { installCompatMount } from './compat/globalMount'
|
||||
import { installLegacyConfigTraps } from './compat/globalConfig'
|
||||
|
||||
export interface App<HostElement = any> {
|
||||
version: string
|
||||
@ -313,126 +306,8 @@ export function createAppAPI<HostElement>(
|
||||
})
|
||||
|
||||
if (__COMPAT__) {
|
||||
/**
|
||||
* 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 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!
|
||||
}
|
||||
installCompatMount(app, context, render, hydrate)
|
||||
installLegacyConfigTraps(app.config)
|
||||
}
|
||||
|
||||
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,
|
||||
extend,
|
||||
isString,
|
||||
warnDeprecation,
|
||||
DeprecationTypes
|
||||
isFunction
|
||||
} from '@vue/shared'
|
||||
import {
|
||||
ReactiveEffect,
|
||||
@ -42,7 +41,7 @@ import { markAttrsAccessed } from './componentRenderUtils'
|
||||
import { currentRenderingInstance } from './componentRenderContext'
|
||||
import { warn } from './warning'
|
||||
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`
|
||||
*
|
||||
@ -356,7 +355,12 @@ export const PublicInstanceProxyHandlers: ProxyHandler<any> = {
|
||||
((globalProperties = appContext.config.globalProperties),
|
||||
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 (
|
||||
__DEV__ &&
|
||||
currentRenderingInstance &&
|
||||
@ -510,7 +514,10 @@ export function createRenderContext(instance: ComponentInternalInstance) {
|
||||
Object.defineProperty(target, key, {
|
||||
configurable: true,
|
||||
enumerable: false,
|
||||
get: () => globalProperties[key],
|
||||
get: () => {
|
||||
const val = globalProperties[key]
|
||||
return __COMPAT__ && isFunction(val) ? val.bind(instance.proxy) : val
|
||||
},
|
||||
set: NOOP
|
||||
})
|
||||
})
|
||||
|
@ -279,3 +279,10 @@ const _ssrUtils = {
|
||||
* @internal
|
||||
*/
|
||||
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,
|
||||
App,
|
||||
RootHydrateFunction,
|
||||
isRuntimeOnly
|
||||
isRuntimeOnly,
|
||||
warnDeprecation,
|
||||
DeprecationTypes
|
||||
} from '@vue/runtime-core'
|
||||
import { nodeOps } from './nodeOps'
|
||||
import { patchProp, forcePatchProp } from './patchProp'
|
||||
// Importing from the compiler, will be tree-shaken in prod
|
||||
import {
|
||||
isFunction,
|
||||
isString,
|
||||
isHTMLTag,
|
||||
isSVGTag,
|
||||
extend,
|
||||
warnDeprecation,
|
||||
DeprecationTypes
|
||||
} from '@vue/shared'
|
||||
import { isFunction, isString, isHTMLTag, isSVGTag, extend } from '@vue/shared'
|
||||
|
||||
declare module '@vue/reactivity' {
|
||||
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 './looseEqual'
|
||||
export * from './toDisplayString'
|
||||
export * from './deprecations'
|
||||
|
||||
/**
|
||||
* 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.
|
||||
import { initDev } from './dev'
|
||||
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 { InternalRenderFunction } from 'packages/runtime-core/src/component'
|
||||
import * as runtimeDom from '@vue/runtime-dom'
|
||||
import { Vue } from './apiGlobal'
|
||||
|
||||
if (__DEV__) {
|
||||
initDev()
|
||||
@ -87,6 +91,8 @@ function compileToFunction(
|
||||
|
||||
registerRuntimeCompiler(compileToFunction)
|
||||
|
||||
const Vue = createCompatVue()
|
||||
|
||||
Vue.compile = compileToFunction
|
||||
extend(Vue, runtimeDom)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user