From 1d8f3a264eabf83b40494f101ecf5a870cab0a95 Mon Sep 17 00:00:00 2001 From: Evan You Date: Sat, 25 Jun 2016 10:43:19 -0400 Subject: [PATCH] better global mixin strategy --- flow/component.js | 3 ++ src/core/config.js | 12 +----- src/core/global-api/extend.js | 11 +++-- src/core/global-api/mixin.js | 8 ---- src/core/instance/init.js | 46 ++++++++++++++------- test/unit/features/global-api/mixin.spec.js | 7 ---- 6 files changed, 42 insertions(+), 45 deletions(-) diff --git a/flow/component.js b/flow/component.js index 04cb3ef6..53e5fcb0 100644 --- a/flow/component.js +++ b/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; // assets static directive: (id: string, def?: Function | Object) => Function | Object | void; static component: (id: string, def?: Class | Object) => Class; diff --git a/src/core/config.js b/src/core/config.js index 777183d0..8125497c 100644 --- a/src/core/config.js +++ b/src/core/config.js @@ -18,8 +18,7 @@ export type Config = { _assetTypes: Array, _lifecycleHooks: Array, _maxUpdateCount: number, - _isServer: boolean, - _ctors: Array + _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 diff --git a/src/core/global-api/extend.js b/src/core/global-api/extend.js index c1d5e2ca..f6d6a2d5 100644 --- a/src/core/global-api/extend.js +++ b/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 diff --git a/src/core/global-api/mixin.js b/src/core/global-api/mixin.js index aa8bcf24..cc7b02e4 100644 --- a/src/core/global-api/mixin.js +++ b/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 - } - }) } } diff --git a/src/core/instance/init.js b/src/core/instance/init.js index 84563110..0cd8208f 100644 --- a/src/core/instance/init.js +++ b/src/core/instance/init.js @@ -24,7 +24,7 @@ export function initMixin (Vue: Class) { initInternalComponent(vm, options) } else { vm.$options = mergeOptions( - vm.constructor.options, + resolveConstructorOptions(vm), options || {}, vm ) @@ -44,19 +44,37 @@ export function initMixin (Vue: Class) { 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 } } diff --git a/test/unit/features/global-api/mixin.spec.js b/test/unit/features/global-api/mixin.spec.js index 1d473a57..73518ca9 100644 --- a/test/unit/features/global-api/mixin.spec.js +++ b/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) - }) })