1.本文將講述 方法 Vue.extend Vue.mixin 與 new Vue({mixins:[], extend:{}})的區別與原理
先回顧一下 Vue.mixin 官網如下描述:
Vue.mixin( mixin )全局注冊一個混入,影響注冊之后所有創建的每個 Vue 實例。插件作者可以使用混入,向組件注入自定義的行為。
既然可以影響到注冊后的所有實例,那么該方法注入的方法和屬性都存放在哪里呢(構造函數的options屬性上),我們一起來看看該方法的定義
Vue.mixin = function (mixin) {
//mergeOption,將Vue構造函數的Options屬性與傳入的mixin參數進行合並,
//合並之后再復制給Vue構造函數的Options屬性
this.options = mergeOptions(this.options, mixin);
return this
};
為什么Vue.mixin方法將mixin合並至Vue.options就能影響注冊之后的所有實例呢,讓我們看看Vue實例化的過程(將構造函數的options屬性與實例化參數合並后付給實例的$options屬性
)
1 function Vue(options) { 2 //調用_init方法 3 this._init(options); 4 } 5 6 7 8 Vue.prototype._init = function (options) { 9 var vm = this; 10 // a uid 11 vm._uid = uid$3++; 12 13 var startTag, endTag; 14 21 // a flag to avoid this being observed 標記該對象是一個Vue實例 22 vm._isVue = true; 23 // merge options 24 if (options && options._isComponent) { //組件實例化過程,即Vue.extend返回對象--稍后解釋 25 // optimize internal component instantiation 26 // since dynamic options merging is pretty slow, and none of the 27 // internal component options needs special treatment. 28 initInternalComponent(vm, options); 29 } else {//將構造函數的options屬性與實例化參數合並后付給實例的$options屬性 ,該屬性會在函數initState中進行初始化 30 vm.$options = mergeOptions( 31 resolveConstructorOptions(vm.constructor), 32 options || {}, 33 vm 34 ); 35 } 36 /* istanbul ignore else */ 37 { 38 initProxy(vm); 39 } 40 // expose real self 41 vm._self = vm; 42 initLifecycle(vm); 43 initEvents(vm); 44 initRender(vm); 45 callHook(vm, 'beforeCreate'); 46 initInjections(vm); // resolve injections before data/props 47 initState(vm); 48 initProvide(vm); // resolve provide after data/props 49 callHook(vm, 'created'); 50 51 /* istanbul ignore if */ 52 if ("development" !== 'production' && config.performance && mark) { 53 vm._name = formatComponentName(vm, false); 54 mark(endTag); 55 measure(("vue " + (vm._name) + " init"), startTag, endTag); 56 } 57 58 if (vm.$options.el) { 59 vm.$mount(vm.$options.el); 60 } 61 };
Vue.extend-- 使用基礎 Vue 構造器,創建一個“子類”。參數是一個包含組件選項的對象
該方法返回一個與Vue具有相同功能的構造函數(其實為創建了一個組件)-屬性options是 合並 基礎 Vue 構造器 與 extend的參數 的對象,
Vue.extend = function (extendOptions) { extendOptions = extendOptions || {}; //將調用函數付給Super var Super = this; var SuperId = Super.cid; //如果參數中參入與創建的構造函數則直接返回 var cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {}); if (cachedCtors[SuperId]) { return cachedCtors[SuperId] } //獲取組件的名稱 var name = extendOptions.name || Super.options.name; if ("development" !== 'production' && name) { validateComponentName(name); } //創建組件Sub var Sub = function VueComponent(options) { this._init(options); };
//為組件添加對應的屬性與方法 Sub.prototype = Object.create(Super.prototype); Sub.prototype.constructor = Sub; Sub.cid = cid++;
//合並super的options與extend的入參並賦值給Sub的options屬性 Sub.options = mergeOptions( Super.options, extendOptions );
//在sub上保存Super的信息 Sub['super'] = Super; // For props and computed properties, we define the proxy getters on // the Vue instances at extension time, on the extended prototype. This // avoids Object.defineProperty calls for each instance created. if (Sub.options.props) { initProps$1(Sub); } if (Sub.options.computed) { initComputed$1(Sub); } // allow further extension/mixin/plugin usage Sub.extend = Super.extend; Sub.mixin = Super.mixin; Sub.use = Super.use; // create asset registers, so extended classes // can have their private assets too. ASSET_TYPES.forEach(function (type) { Sub[type] = Super[type]; }); // enable recursive self-lookup
//如果有組件名稱,將該組件掛載到sub.options.components上。以便可在組件內使用
if (name) { Sub.options.components[name] = Sub; } // keep a reference to the super options at extension time. // later at instantiation we can check if Super's options have // been updated.
//保存option信息。以便在render的時候生成最新的options選項
Sub.superOptions = Super.options; Sub.extendOptions = extendOptions; Sub.sealedOptions = extend({}, Sub.options); // cache constructor cachedCtors[SuperId] = Sub; return Sub //返回sub構造函數 };
Vue.component( id, [definition] ) 注冊或獲取全局組件。注冊還會自動使用給定的id設置組件的名稱,第二個參數可以是Object也可以調用Vue.extend 返回的構造函數,返回組件
調用Vue.component 將調用Vue.extend生成一個組件(構造函數),該組件將賦值給Vue.options.components[id],由於在實例化時,將合並構造函數的options至實例對象的$options上,所有通過通過全局構造函數Vue創建的組件或者視圖都可以運用該組件,
var ASSET_TYPES = [ 'component', 'directive', 'filter' ]; //定義 Vue.component . Vue.directive Vue.filter ASSET_TYPES.forEach(function (type) { Vue[type] = function ( id, definition ) { if (!definition) { return this.options[type + 's'][id] } else { /* istanbul ignore if */ if ("development" !== 'production' && type === 'component') { //對組件的命名規范進行驗證 validateComponentName(id); } if (type === 'component' && isPlainObject(definition)) { //如果第二個參數是一個Object 調用 Vue.extend(definition)生成一個構造函數 this.options._base === Vue definition.name = definition.name || id; definition = this.options._base.extend(definition); } if (type === 'directive' && typeof definition === 'function') { definition = { bind: definition, update: definition }; } //將Vue.options.components[id]賦值為 組件構造函數 this.options[type + 's'][id] = definition; return definition } }; }); }
由以上代碼可見 Vue.component返回的組件即為 Vue.extend創建的構造函數 -再次命名為subVue,subVue具有與Vue一樣的方法和屬性,所以可調用subVue.component,subVue.extend創建組件,調用subVue.component,subVue.extend創建的組件組件將存放於subVue.options上,所以在通過subVue創建的組件,只能用於subVue實例化傳入字符串模板的中模板使用,或者subVue.component,subVue.extend創建的組件實例化傳入的字符串模板中使用。
new Vue({mixins:[], extend:{}})
實例化選項mixins , extend將在init函數中通過mergeOptions合並至實例屬性$options,他們的區別是extend為options對象,mixins為options數組,同時 extend的方法將比mixins先執行,但他們都會在 Vue.extend 與 Vue.mixin之后 執行
