wip: restructure compat code + global api alignment

This commit is contained in:
Evan You 2021-04-05 17:09:22 -04:00
parent e2d6ff845b
commit 40e3dd28e1
13 changed files with 499 additions and 364 deletions

View File

@ -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'],

View File

@ -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

View 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}` : ``
}`
)
}

View 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
}

View 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
}
})
})
}

View 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!
}
}

View File

@ -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
})
})

View File

@ -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'

View File

@ -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 {

View File

@ -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}` : ``}`
)
}

View File

@ -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

View File

@ -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

View File

@ -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)