From: Evan You Date: Wed, 5 May 2021 21:56:09 +0000 (-0400) Subject: wip: make singleton mutations affect all app instances X-Git-Tag: v3.1.0-beta.1~33 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f2a5a3ee55bf022c3114124df945560e4c5b5e0f;p=thirdparty%2Fvuejs%2Fcore.git wip: make singleton mutations affect all app instances --- diff --git a/packages/runtime-core/src/apiCreateApp.ts b/packages/runtime-core/src/apiCreateApp.ts index 52a2a0b2cf..b134a482bf 100644 --- a/packages/runtime-core/src/apiCreateApp.ts +++ b/packages/runtime-core/src/apiCreateApp.ts @@ -15,7 +15,7 @@ import { RootHydrateFunction } from './hydration' import { devtoolsInitApp, devtoolsUnmountApp } from './devtools' import { isFunction, NO, isObject } from '@vue/shared' import { version } from '.' -import { installCompatMount } from './compat/global' +import { applySingletonAppMutations, installCompatMount } from './compat/global' import { installLegacyConfigProperties } from './compat/globalConfig' import { installGlobalFilterMethod } from './compat/filter' @@ -331,6 +331,7 @@ export function createAppAPI( installCompatMount(app, context, render, hydrate) installGlobalFilterMethod(app, context) if (__DEV__) installLegacyConfigProperties(app.config) + applySingletonAppMutations(app) } return app diff --git a/packages/runtime-core/src/compat/global.ts b/packages/runtime-core/src/compat/global.ts index 0949d5971d..9f353d414d 100644 --- a/packages/runtime-core/src/compat/global.ts +++ b/packages/runtime-core/src/compat/global.ts @@ -115,15 +115,22 @@ export type CompatVue = Pick & { export let isCopyingConfig = false +// exported only for test +export let singletonApp: App +let singletonCtor: Function + // Legacy global Vue constructor export function createCompatVue( - createApp: CreateAppFunction + createApp: CreateAppFunction, + createSingletonApp: CreateAppFunction ): CompatVue { - const Vue: CompatVue = function Vue(options: ComponentOptions = {}) { - return createCompatApp(options, Vue) - } as any + singletonApp = createSingletonApp({}) - const singletonApp = createApp({}) + const Vue: CompatVue = (singletonCtor = function Vue( + options: ComponentOptions = {} + ) { + return createCompatApp(options, Vue) + } as any) function createCompatApp(options: ComponentOptions = {}, Ctor: any) { assertCompatEnabled(DeprecationTypes.GLOBAL_MOUNT, null) @@ -139,53 +146,8 @@ export function createCompatVue( const app = createApp(options) - // copy over asset registries and deopt flag - ;['mixins', 'components', 'directives', 'filters', 'deopt'].forEach(key => { - // @ts-ignore - app._context[key] = singletonApp._context[key] - }) - - // copy over global config mutations - isCopyingConfig = true - for (const key in singletonApp.config) { - if (key === 'isNativeTag') continue - if ( - isRuntimeOnly() && - (key === 'isCustomElement' || key === 'compilerOptions') - ) { - continue - } - const val = singletonApp.config[key as keyof AppConfig] - // @ts-ignore - app.config[key] = val - - // compat for runtime ignoredElements -> isCustomElement - if ( - key === 'ignoredElements' && - isCompatEnabled(DeprecationTypes.CONFIG_IGNORED_ELEMENTS, null) && - !isRuntimeOnly() && - isArray(val) - ) { - app.config.compilerOptions.isCustomElement = tag => { - return val.some(v => (isString(v) ? v === tag : v.test(tag))) - } - } - } - isCopyingConfig = false - - // copy prototype augmentations as config.globalProperties - if (isCompatEnabled(DeprecationTypes.GLOBAL_PROTOTYPE, null)) { - app.config.globalProperties = Ctor.prototype - } - let hasPrototypeAugmentations = false - for (const key in Ctor.prototype) { - if (key !== 'constructor') { - hasPrototypeAugmentations = true - break - } - } - if (__DEV__ && hasPrototypeAugmentations) { - warnDeprecation(DeprecationTypes.GLOBAL_PROTOTYPE, null) + if (Ctor !== Vue) { + applySingletonPrototype(app, Ctor) } const vm = app._createRoot!(options) @@ -348,6 +310,66 @@ export function createCompatVue( return Vue } +export function applySingletonAppMutations(app: App, Ctor?: Function) { + if (!singletonApp) { + // this is the call of creating the singleton itself + return + } + + // copy over asset registries and deopt flag + ;['mixins', 'components', 'directives', 'filters', 'deopt'].forEach(key => { + // @ts-ignore + app._context[key] = singletonApp._context[key] + }) + + // copy over global config mutations + isCopyingConfig = true + for (const key in singletonApp.config) { + if (key === 'isNativeTag') continue + if ( + isRuntimeOnly() && + (key === 'isCustomElement' || key === 'compilerOptions') + ) { + continue + } + const val = singletonApp.config[key as keyof AppConfig] + // @ts-ignore + app.config[key] = val + + // compat for runtime ignoredElements -> isCustomElement + if ( + key === 'ignoredElements' && + isCompatEnabled(DeprecationTypes.CONFIG_IGNORED_ELEMENTS, null) && + !isRuntimeOnly() && + isArray(val) + ) { + app.config.compilerOptions.isCustomElement = tag => { + return val.some(v => (isString(v) ? v === tag : v.test(tag))) + } + } + } + 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 + } + let hasPrototypeAugmentations = false + for (const key in Ctor.prototype) { + if (key !== 'constructor') { + hasPrototypeAugmentations = true + break + } + } + if (__DEV__ && hasPrototypeAugmentations) { + warnDeprecation(DeprecationTypes.GLOBAL_PROTOTYPE, null) + } +} + export function installCompatMount( app: App, context: AppContext, diff --git a/packages/vue-compat/__tests__/global.spec.ts b/packages/vue-compat/__tests__/global.spec.ts index ad3619dd64..3cd2c9a18d 100644 --- a/packages/vue-compat/__tests__/global.spec.ts +++ b/packages/vue-compat/__tests__/global.spec.ts @@ -6,6 +6,8 @@ import { deprecationData, toggleDeprecationWarning } from '../../runtime-core/src/compat/compatConfig' +import { singletonApp } from '../../runtime-core/src/compat/global' +import { createApp } from '../src/esm-index' beforeEach(() => { toggleDeprecationWarning(false) @@ -280,6 +282,15 @@ describe('GLOBAL_PROTOTYPE', () => { const plain = new Vue() as any expect(plain.$test).toBeUndefined() }) + + test('should affect apps created via createApp()', () => { + Vue.prototype.$test = 1 + const vm = createApp({ + template: 'foo' + }).mount(document.createElement('div')) as any + expect(vm.$test).toBe(1) + delete Vue.prototype.$test + }) }) describe('GLOBAL_SET/DELETE', () => { @@ -381,3 +392,12 @@ describe('GLOBAL_PRIVATE_UTIL', () => { expect(n).toBe(2) }) }) + +test('global asset registration should affect apps created via createApp', () => { + Vue.component('foo', { template: 'foo' }) + const vm = createApp({ + template: '' + }).mount(document.createElement('div')) as any + expect(vm.$el.textContent).toBe('foo') + delete singletonApp._context.components.foo +}) diff --git a/packages/vue-compat/__tests__/globalConfig.spec.ts b/packages/vue-compat/__tests__/globalConfig.spec.ts index b51b30da20..af02a218ff 100644 --- a/packages/vue-compat/__tests__/globalConfig.spec.ts +++ b/packages/vue-compat/__tests__/globalConfig.spec.ts @@ -1,5 +1,6 @@ import Vue from '@vue/compat' import { toggleDeprecationWarning } from '../../runtime-core/src/compat/compatConfig' +import { createApp } from '../src/esm-index' import { triggerEvent } from './utils' beforeEach(() => { @@ -64,3 +65,12 @@ test('GLOBAL_IGNORED_ELEMENTS', () => { }) expect(el.innerHTML).toBe(``) }) + +test('singleton config should affect apps created with createApp()', () => { + Vue.config.ignoredElements = [/^v-/, 'foo'] + const el = document.createElement('div') + createApp({ + template: `` + }).mount(el) + expect(el.innerHTML).toBe(``) +}) diff --git a/packages/vue-compat/src/createCompatVue.ts b/packages/vue-compat/src/createCompatVue.ts index c9c2b86791..5815fc21dc 100644 --- a/packages/vue-compat/src/createCompatVue.ts +++ b/packages/vue-compat/src/createCompatVue.ts @@ -38,9 +38,7 @@ function wrappedCreateApp(...args: any[]) { } export function createCompatVue() { - const Vue = compatUtils.createCompatVue(wrappedCreateApp) + const Vue = compatUtils.createCompatVue(createApp, wrappedCreateApp) extend(Vue, runtimeDom) - // @ts-ignore - Vue.createApp = wrappedCreateApp return Vue }