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