]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
wip: make singleton mutations affect all app instances
authorEvan You <yyx990803@gmail.com>
Wed, 5 May 2021 21:56:09 +0000 (17:56 -0400)
committerEvan You <yyx990803@gmail.com>
Wed, 5 May 2021 21:56:09 +0000 (17:56 -0400)
packages/runtime-core/src/apiCreateApp.ts
packages/runtime-core/src/compat/global.ts
packages/vue-compat/__tests__/global.spec.ts
packages/vue-compat/__tests__/globalConfig.spec.ts
packages/vue-compat/src/createCompatVue.ts

index 52a2a0b2cf060f142d2a59d5e2c41a718aacbc1f..b134a482bff7bc4bb90f18b6208b7f9df07d2bfa 100644 (file)
@@ -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<HostElement>(
       installCompatMount(app, context, render, hydrate)
       installGlobalFilterMethod(app, context)
       if (__DEV__) installLegacyConfigProperties(app.config)
+      applySingletonAppMutations(app)
     }
 
     return app
index 0949d5971d2a6c9e25f97383b801f22874fc4948..9f353d414d82cef49574edd28aab24d3a5b67068 100644 (file)
@@ -115,15 +115,22 @@ export type CompatVue = Pick<App, 'version' | 'component' | 'directive'> & {
 
 export let isCopyingConfig = false
 
+// exported only for test
+export let singletonApp: App
+let singletonCtor: Function
+
 // Legacy global Vue constructor
 export function createCompatVue(
-  createApp: CreateAppFunction<Element>
+  createApp: CreateAppFunction<Element>,
+  createSingletonApp: CreateAppFunction<Element>
 ): 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,
index ad3619dd64111ccf5a9ee0c33319c1518c8618a3..3cd2c9a18df9d1396b3fbf1416023d8b6dade572 100644 (file)
@@ -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: '<foo/>'
+  }).mount(document.createElement('div')) as any
+  expect(vm.$el.textContent).toBe('foo')
+  delete singletonApp._context.components.foo
+})
index b51b30da206a053e24df540197fc63cb95daf429..af02a218ffe47409b41f0c42ada4e611506c7225 100644 (file)
@@ -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(`<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>`)
+})
index c9c2b8679177a344bad3edc083a8fe5e06686547..5815fc21dc6b2c8f02c181255312507c6c6fecfb 100644 (file)
@@ -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
 }