wip: make singleton mutations affect all app instances
This commit is contained in:
parent
61edb700d7
commit
f2a5a3ee55
@ -15,7 +15,7 @@ import { RootHydrateFunction } from './hydration'
|
|||||||
import { devtoolsInitApp, devtoolsUnmountApp } from './devtools'
|
import { devtoolsInitApp, devtoolsUnmountApp } from './devtools'
|
||||||
import { isFunction, NO, isObject } from '@vue/shared'
|
import { isFunction, NO, isObject } from '@vue/shared'
|
||||||
import { version } from '.'
|
import { version } from '.'
|
||||||
import { installCompatMount } from './compat/global'
|
import { applySingletonAppMutations, installCompatMount } from './compat/global'
|
||||||
import { installLegacyConfigProperties } from './compat/globalConfig'
|
import { installLegacyConfigProperties } from './compat/globalConfig'
|
||||||
import { installGlobalFilterMethod } from './compat/filter'
|
import { installGlobalFilterMethod } from './compat/filter'
|
||||||
|
|
||||||
@ -331,6 +331,7 @@ export function createAppAPI<HostElement>(
|
|||||||
installCompatMount(app, context, render, hydrate)
|
installCompatMount(app, context, render, hydrate)
|
||||||
installGlobalFilterMethod(app, context)
|
installGlobalFilterMethod(app, context)
|
||||||
if (__DEV__) installLegacyConfigProperties(app.config)
|
if (__DEV__) installLegacyConfigProperties(app.config)
|
||||||
|
applySingletonAppMutations(app)
|
||||||
}
|
}
|
||||||
|
|
||||||
return app
|
return app
|
||||||
|
@ -115,15 +115,22 @@ export type CompatVue = Pick<App, 'version' | 'component' | 'directive'> & {
|
|||||||
|
|
||||||
export let isCopyingConfig = false
|
export let isCopyingConfig = false
|
||||||
|
|
||||||
|
// exported only for test
|
||||||
|
export let singletonApp: App
|
||||||
|
let singletonCtor: Function
|
||||||
|
|
||||||
// Legacy global Vue constructor
|
// Legacy global Vue constructor
|
||||||
export function createCompatVue(
|
export function createCompatVue(
|
||||||
createApp: CreateAppFunction<Element>
|
createApp: CreateAppFunction<Element>,
|
||||||
|
createSingletonApp: CreateAppFunction<Element>
|
||||||
): CompatVue {
|
): CompatVue {
|
||||||
const Vue: CompatVue = function Vue(options: ComponentOptions = {}) {
|
singletonApp = createSingletonApp({})
|
||||||
return createCompatApp(options, Vue)
|
|
||||||
} as any
|
|
||||||
|
|
||||||
const singletonApp = createApp({})
|
const Vue: CompatVue = (singletonCtor = function Vue(
|
||||||
|
options: ComponentOptions = {}
|
||||||
|
) {
|
||||||
|
return createCompatApp(options, Vue)
|
||||||
|
} as any)
|
||||||
|
|
||||||
function createCompatApp(options: ComponentOptions = {}, Ctor: any) {
|
function createCompatApp(options: ComponentOptions = {}, Ctor: any) {
|
||||||
assertCompatEnabled(DeprecationTypes.GLOBAL_MOUNT, null)
|
assertCompatEnabled(DeprecationTypes.GLOBAL_MOUNT, null)
|
||||||
@ -139,53 +146,8 @@ export function createCompatVue(
|
|||||||
|
|
||||||
const app = createApp(options)
|
const app = createApp(options)
|
||||||
|
|
||||||
// copy over asset registries and deopt flag
|
if (Ctor !== Vue) {
|
||||||
;['mixins', 'components', 'directives', 'filters', 'deopt'].forEach(key => {
|
applySingletonPrototype(app, Ctor)
|
||||||
// @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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const vm = app._createRoot!(options)
|
const vm = app._createRoot!(options)
|
||||||
@ -348,6 +310,66 @@ export function createCompatVue(
|
|||||||
return Vue
|
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(
|
export function installCompatMount(
|
||||||
app: App,
|
app: App,
|
||||||
context: AppContext,
|
context: AppContext,
|
||||||
|
@ -6,6 +6,8 @@ import {
|
|||||||
deprecationData,
|
deprecationData,
|
||||||
toggleDeprecationWarning
|
toggleDeprecationWarning
|
||||||
} from '../../runtime-core/src/compat/compatConfig'
|
} from '../../runtime-core/src/compat/compatConfig'
|
||||||
|
import { singletonApp } from '../../runtime-core/src/compat/global'
|
||||||
|
import { createApp } from '../src/esm-index'
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
toggleDeprecationWarning(false)
|
toggleDeprecationWarning(false)
|
||||||
@ -280,6 +282,15 @@ describe('GLOBAL_PROTOTYPE', () => {
|
|||||||
const plain = new Vue() as any
|
const plain = new Vue() as any
|
||||||
expect(plain.$test).toBeUndefined()
|
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', () => {
|
describe('GLOBAL_SET/DELETE', () => {
|
||||||
@ -381,3 +392,12 @@ describe('GLOBAL_PRIVATE_UTIL', () => {
|
|||||||
expect(n).toBe(2)
|
expect(n).toBe(2)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('global asset registration should affect apps created via createApp', () => {
|
||||||
|
Vue.component('foo', { template: 'foo' })
|
||||||
|
const vm = createApp({
|
||||||
|
template: '<foo/>'
|
||||||
|
}).mount(document.createElement('div')) as any
|
||||||
|
expect(vm.$el.textContent).toBe('foo')
|
||||||
|
delete singletonApp._context.components.foo
|
||||||
|
})
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import Vue from '@vue/compat'
|
import Vue from '@vue/compat'
|
||||||
import { toggleDeprecationWarning } from '../../runtime-core/src/compat/compatConfig'
|
import { toggleDeprecationWarning } from '../../runtime-core/src/compat/compatConfig'
|
||||||
|
import { createApp } from '../src/esm-index'
|
||||||
import { triggerEvent } from './utils'
|
import { triggerEvent } from './utils'
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@ -64,3 +65,12 @@ test('GLOBAL_IGNORED_ELEMENTS', () => {
|
|||||||
})
|
})
|
||||||
expect(el.innerHTML).toBe(`<v-foo></v-foo><foo></foo>`)
|
expect(el.innerHTML).toBe(`<v-foo></v-foo><foo></foo>`)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('singleton config should affect apps created with createApp()', () => {
|
||||||
|
Vue.config.ignoredElements = [/^v-/, 'foo']
|
||||||
|
const el = document.createElement('div')
|
||||||
|
createApp({
|
||||||
|
template: `<v-foo/><foo/>`
|
||||||
|
}).mount(el)
|
||||||
|
expect(el.innerHTML).toBe(`<v-foo></v-foo><foo></foo>`)
|
||||||
|
})
|
||||||
|
@ -38,9 +38,7 @@ function wrappedCreateApp(...args: any[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function createCompatVue() {
|
export function createCompatVue() {
|
||||||
const Vue = compatUtils.createCompatVue(wrappedCreateApp)
|
const Vue = compatUtils.createCompatVue(createApp, wrappedCreateApp)
|
||||||
extend(Vue, runtimeDom)
|
extend(Vue, runtimeDom)
|
||||||
// @ts-ignore
|
|
||||||
Vue.createApp = wrappedCreateApp
|
|
||||||
return Vue
|
return Vue
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user