From b10db86711b75a6c2402b80fee42cad2db4843dc Mon Sep 17 00:00:00 2001 From: Evan You Date: Thu, 6 May 2021 17:15:11 -0400 Subject: [PATCH] wip: make legacy global apis available on app instance as well --- packages/runtime-core/src/apiCreateApp.ts | 9 +- .../runtime-core/src/compat/compatConfig.ts | 2 +- packages/runtime-core/src/compat/filter.ts | 18 -- packages/runtime-core/src/compat/global.ts | 164 +++++++++++++----- 4 files changed, 120 insertions(+), 73 deletions(-) delete mode 100644 packages/runtime-core/src/compat/filter.ts diff --git a/packages/runtime-core/src/apiCreateApp.ts b/packages/runtime-core/src/apiCreateApp.ts index b134a482..a6736b79 100644 --- a/packages/runtime-core/src/apiCreateApp.ts +++ b/packages/runtime-core/src/apiCreateApp.ts @@ -15,9 +15,7 @@ import { RootHydrateFunction } from './hydration' import { devtoolsInitApp, devtoolsUnmountApp } from './devtools' import { isFunction, NO, isObject } from '@vue/shared' import { version } from '.' -import { applySingletonAppMutations, installCompatMount } from './compat/global' -import { installLegacyConfigProperties } from './compat/globalConfig' -import { installGlobalFilterMethod } from './compat/filter' +import { installAppCompatProperties } from './compat/global' export interface App { version: string @@ -328,10 +326,7 @@ export function createAppAPI( }) if (__COMPAT__) { - installCompatMount(app, context, render, hydrate) - installGlobalFilterMethod(app, context) - if (__DEV__) installLegacyConfigProperties(app.config) - applySingletonAppMutations(app) + installAppCompatProperties(app, context, render) } return app diff --git a/packages/runtime-core/src/compat/compatConfig.ts b/packages/runtime-core/src/compat/compatConfig.ts index e786cf09..9bcace7c 100644 --- a/packages/runtime-core/src/compat/compatConfig.ts +++ b/packages/runtime-core/src/compat/compatConfig.ts @@ -97,7 +97,7 @@ export const deprecationData: Record = { [DeprecationTypes.GLOBAL_PROTOTYPE]: { message: `Vue.prototype is no longer available in Vue 3. ` + - `Use config.globalProperties instead.`, + `Use app.config.globalProperties instead.`, link: `https://v3.vuejs.org/guide/migration/global-api.html#vue-prototype-replaced-by-config-globalproperties` }, diff --git a/packages/runtime-core/src/compat/filter.ts b/packages/runtime-core/src/compat/filter.ts deleted file mode 100644 index 29a5a3fc..00000000 --- a/packages/runtime-core/src/compat/filter.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { App, AppContext } from '../apiCreateApp' -import { warn } from '../warning' -import { assertCompatEnabled, DeprecationTypes } from './compatConfig' - -export function installGlobalFilterMethod(app: App, context: AppContext) { - context.filters = {} - app.filter = (name: string, filter?: Function): any => { - assertCompatEnabled(DeprecationTypes.FILTERS, null) - if (!filter) { - return context.filters![name] - } - if (__DEV__ && context.filters![name]) { - warn(`Filter "${name}" has already been registered.`) - } - context.filters![name] = filter - return app - } -} diff --git a/packages/runtime-core/src/compat/global.ts b/packages/runtime-core/src/compat/global.ts index 24667066..da32009f 100644 --- a/packages/runtime-core/src/compat/global.ts +++ b/packages/runtime-core/src/compat/global.ts @@ -19,7 +19,6 @@ import { import { warn } from '../warning' import { cloneVNode, createVNode } from '../vnode' import { RootRenderFunction } from '../renderer' -import { RootHydrateFunction } from '../hydration' import { App, AppConfig, @@ -41,7 +40,11 @@ import { devtoolsInitApp, devtoolsUnmountApp } from '../devtools' import { Directive } from '../directives' import { nextTick } from '../scheduler' import { version } from '..' -import { LegacyConfig, legacyOptionMergeStrats } from './globalConfig' +import { + installLegacyConfigProperties, + LegacyConfig, + legacyOptionMergeStrats +} from './globalConfig' import { LegacyDirective } from './customDirective' import { warnDeprecation, @@ -69,7 +72,6 @@ export type CompatVue = Pick & { version: string config: AppConfig & LegacyConfig - extend: (options?: ComponentOptions) => CompatVue nextTick: typeof nextTick use(plugin: Plugin, ...options: any[]): CompatVue @@ -82,6 +84,10 @@ export type CompatVue = Pick & { compile(template: string): RenderFunction + /** + * @deprecated + */ + extend: (options?: ComponentOptions) => CompatVue /** * @deprecated Vue 3 no longer needs set() for adding new properties. */ @@ -106,6 +112,10 @@ export type CompatVue = Pick & { * @internal */ options: ComponentOptions + /** + * @internal + */ + util: any /** * @internal */ @@ -116,7 +126,7 @@ export let isCopyingConfig = false // exported only for test export let singletonApp: App -let singletonCtor: Function +let singletonCtor: CompatVue // Legacy global Vue constructor export function createCompatVue( @@ -159,12 +169,46 @@ export function createCompatVue( Vue.version = __VERSION__ Vue.config = singletonApp.config - Vue.nextTick = nextTick + + Vue.use = (p, ...options) => { + if (p && isFunction(p.install)) { + p.install(Vue as any, ...options) + } else if (isFunction(p)) { + p(Vue as any, ...options) + } + return Vue + } + + Vue.mixin = m => { + singletonApp.mixin(m) + return Vue + } + + Vue.component = ((name: string, comp: Component) => { + if (comp) { + singletonApp.component(name, comp) + return Vue + } else { + return singletonApp.component(name) + } + }) as any + + Vue.directive = ((name: string, dir: Directive | LegacyDirective) => { + if (dir) { + singletonApp.directive(name, dir as Directive) + return Vue + } else { + return singletonApp.directive(name) + } + }) as any + Vue.options = { _base: Vue } let cid = 1 Vue.cid = cid + Vue.nextTick = nextTick + const extendCache = new WeakMap() function extendCtor(this: any, extendOptions: ComponentOptions = {}) { @@ -243,38 +287,6 @@ export function createCompatVue( return reactive(target) } - Vue.use = (p, ...options) => { - if (p && isFunction(p.install)) { - p.install(Vue as any, ...options) - } else if (isFunction(p)) { - p(Vue as any, ...options) - } - return Vue - } - - Vue.mixin = m => { - singletonApp.mixin(m) - return Vue - } - - Vue.component = ((name: string, comp: Component) => { - if (comp) { - singletonApp.component(name, comp) - return Vue - } else { - return singletonApp.component(name) - } - }) as any - - Vue.directive = ((name: string, dir: Directive | LegacyDirective) => { - if (dir) { - singletonApp.directive(name, dir as Directive) - return Vue - } else { - return singletonApp.directive(name) - } - }) as any - Vue.filter = ((name: string, filter?: any) => { if (filter) { singletonApp.filter!(name, filter) @@ -309,12 +321,64 @@ export function createCompatVue( return Vue } -export function applySingletonAppMutations(app: App, Ctor?: Function) { +export function installAppCompatProperties( + app: App, + context: AppContext, + render: RootRenderFunction +) { + installFilterMethod(app, context) + if (!singletonApp) { - // this is the call of creating the singleton itself + // this is the call of creating the singleton itself so the rest is + // unnecessary return } + installCompatMount(app, context, render) + installLegacyAPIs(app) + applySingletonAppMutations(app) + if (__DEV__) installLegacyConfigProperties(app.config) +} + +function installFilterMethod(app: App, context: AppContext) { + context.filters = {} + app.filter = (name: string, filter?: Function): any => { + assertCompatEnabled(DeprecationTypes.FILTERS, null) + if (!filter) { + return context.filters![name] + } + if (__DEV__ && context.filters![name]) { + warn(`Filter "${name}" has already been registered.`) + } + context.filters![name] = filter + return app + } +} + +function installLegacyAPIs(app: App) { + // expose global API on app instance for legacy plugins + Object.defineProperties(app, { + // so that app.use() can work with legacy plugins that extend prototypes + prototype: { + get() { + __DEV__ && warnDeprecation(DeprecationTypes.GLOBAL_PROTOTYPE, null) + return app.config.globalProperties + } + }, + nextTick: { value: nextTick }, + extend: { value: singletonCtor.extend }, + set: { value: singletonCtor.set }, + delete: { value: singletonCtor.delete }, + observable: { value: singletonCtor.observable }, + util: { + get() { + return singletonCtor.util + } + } + }) +} + +function applySingletonAppMutations(app: App) { // copy over asset registries and deopt flag ;['mixins', 'components', 'directives', 'filters', 'deopt'].forEach(key => { // @ts-ignore @@ -348,20 +412,27 @@ export function applySingletonAppMutations(app: App, Ctor?: Function) { } } isCopyingConfig = false - applySingletonPrototype(app, singletonCtor) } function applySingletonPrototype(app: App, Ctor: Function) { // copy prototype augmentations as config.globalProperties - if (isCompatEnabled(DeprecationTypes.GLOBAL_PROTOTYPE, null)) { - app.config.globalProperties = Ctor.prototype + const enabled = isCompatEnabled(DeprecationTypes.GLOBAL_PROTOTYPE, null) + if (enabled) { + app.config.globalProperties = Object.create(Ctor.prototype) } let hasPrototypeAugmentations = false - for (const key in Ctor.prototype) { + const descriptors = Object.getOwnPropertyDescriptors(Ctor.prototype) + for (const key in descriptors) { if (key !== 'constructor') { hasPrototypeAugmentations = true - break + if (enabled) { + Object.defineProperty( + app.config.globalProperties, + key, + descriptors[key] + ) + } } } if (__DEV__ && hasPrototypeAugmentations) { @@ -369,11 +440,10 @@ function applySingletonPrototype(app: App, Ctor: Function) { } } -export function installCompatMount( +function installCompatMount( app: App, context: AppContext, - render: RootRenderFunction, - hydrate?: RootHydrateFunction + render: RootRenderFunction ) { let isMounted = false