Browse Source

better global mixin strategy

Evan You 10 years ago
parent
commit
1d8f3a264e

+ 3 - 0
flow/component.js

@@ -8,6 +8,9 @@ declare interface Component {
   static options: Object;
   // extend
   static extend: (options: Object) => Function;
+  static superOptions: Object;
+  static extendOptions: Object;
+  static super: Class<Component>;
   // assets
   static directive: (id: string, def?: Function | Object) => Function | Object | void;
   static component: (id: string, def?: Class<Component> | Object) => Class<Component>;

+ 2 - 10
src/core/config.js

@@ -18,8 +18,7 @@ export type Config = {
   _assetTypes: Array<string>,
   _lifecycleHooks: Array<string>,
   _maxUpdateCount: number,
-  _isServer: boolean,
-  _ctors: Array<Function>
+  _isServer: boolean
 }
 
 const config: Config = {
@@ -105,14 +104,7 @@ const config: Config = {
   /**
    * Server rendering?
    */
-  _isServer: process.env.VUE_ENV === 'server',
-
-  /**
-   * Keeping track of all extended Component constructors
-   * so that we can update them in the case of global mixins being applied
-   * after their creation.
-   */
-  _ctors: []
+  _isServer: process.env.VUE_ENV === 'server'
 }
 
 export default config

+ 5 - 6
src/core/global-api/extend.js

@@ -1,7 +1,7 @@
 /* @flow */
 
 import config from '../config'
-import { warn, remove, mergeOptions } from '../util/index'
+import { warn, mergeOptions } from '../util/index'
 
 export function initExtend (Vue: GlobalAPI) {
   /**
@@ -54,12 +54,11 @@ export function initExtend (Vue: GlobalAPI) {
     if (name) {
       Sub.options.components[name] = Sub
     }
-    // book-keeping for global mixin edge cases. also expose a way to remove it
+    // keep a reference to the super options at extension time.
+    // later at instantiation we can check if Super's options have
+    // been updated.
+    Sub.superOptions = Super.options
     Sub.extendOptions = extendOptions
-    config._ctors.push(Sub)
-    Sub.release = () => {
-      remove(config._ctors, Sub)
-    }
     // cache constructor
     if (isFirstExtend) {
       extendOptions._Ctor = Sub

+ 0 - 8
src/core/global-api/mixin.js

@@ -1,17 +1,9 @@
 /* @flow */
 
-import config from '../config'
 import { mergeOptions } from '../util/index'
 
 export function initMixin (Vue: GlobalAPI) {
   Vue.mixin = function (mixin: Object) {
     Vue.options = mergeOptions(Vue.options, mixin)
-    // update constructors that are already created
-    config._ctors.forEach(Ctor => {
-      Ctor.options = mergeOptions(Ctor['super'].options, Ctor.extendOptions)
-      if (Ctor.options.name) {
-        Ctor.options.components[Ctor.options.name] = Ctor
-      }
-    })
   }
 }

+ 32 - 14
src/core/instance/init.js

@@ -24,7 +24,7 @@ export function initMixin (Vue: Class<Component>) {
       initInternalComponent(vm, options)
     } else {
       vm.$options = mergeOptions(
-        vm.constructor.options,
+        resolveConstructorOptions(vm),
         options || {},
         vm
       )
@@ -44,19 +44,37 @@ export function initMixin (Vue: Class<Component>) {
     callHook(vm, 'created')
     initRender(vm)
   }
-}
 
-function initInternalComponent (vm: Component, options: InternalComponentOptions) {
-  const opts = vm.$options = Object.create(vm.constructor.options)
-  // doing this because it's faster than dynamic enumeration.
-  opts.parent = options.parent
-  opts.propsData = options.propsData
-  opts._parentVnode = options._parentVnode
-  opts._parentListeners = options._parentListeners
-  opts._renderChildren = options._renderChildren
-  opts._componentTag = options._componentTag
-  if (options.render) {
-    opts.render = options.render
-    opts.staticRenderFns = options.staticRenderFns
+  function initInternalComponent (vm: Component, options: InternalComponentOptions) {
+    const opts = vm.$options = Object.create(resolveConstructorOptions(vm))
+    // doing this because it's faster than dynamic enumeration.
+    opts.parent = options.parent
+    opts.propsData = options.propsData
+    opts._parentVnode = options._parentVnode
+    opts._parentListeners = options._parentListeners
+    opts._renderChildren = options._renderChildren
+    opts._componentTag = options._componentTag
+    if (options.render) {
+      opts.render = options.render
+      opts.staticRenderFns = options.staticRenderFns
+    }
+  }
+
+  function resolveConstructorOptions (vm: Component) {
+    const Ctor = vm.constructor
+    let options = Ctor.options
+    if (Ctor.super) {
+      const superOptions = Ctor.super.options
+      const cachedSuperOptions = Ctor.superOptions
+      if (superOptions !== cachedSuperOptions) {
+        // super option changed
+        Ctor.superOptions = superOptions
+        options = Ctor.options = mergeOptions(superOptions, Ctor.extendOptions)
+        if (options.name) {
+          options.components[options.name] = Ctor
+        }
+      }
+    }
+    return options
   }
 }

+ 0 - 7
test/unit/features/global-api/mixin.spec.js

@@ -35,11 +35,4 @@ describe('Global API: mixin', () => {
     })
     expect(calls).toEqual(['hello global', 'hello local'])
   })
-
-  it('should allow releasing constructors', () => {
-    const Test = Vue.extend({})
-    expect(Vue.config._ctors.indexOf(Test) > -1).toBe(true)
-    Test.release()
-    expect(Vue.config._ctors.indexOf(Test) > -1).toBe(false)
-  })
 })