【Vue】Vue原始碼解讀之Component元件註冊
阿新 • • 發佈:2020-09-15
Vue可以有全域性註冊和區域性註冊兩種方式來註冊元件。
全域性註冊
註冊方式
全域性註冊有以下兩種註冊方式:
- 通過Vue.component 直接註冊。
Vue.component('button-counter', { //data選項必須是一個函式 data: function () { return { count: 0 } }, template:'#clickBtn' })
- 通過Vue.extend來註冊。
var buttonComponent = Vue.extend({ name:'button-counter', data: function () { return { count: 0 } }, template:'#clickBtn' }); Vue.component('button-counter', buttonComponent);
原始碼實現過程
Vue初始化時,initGlobalAPI通過呼叫initAssetRegisters()進行元件註冊。(原始碼位置:src/core/global-api/assets.js)
functioninitAssetRegisters (Vue: GlobalAPI) { /** * Create asset registration methods. */ ASSET_TYPES.forEach(type => { Vue[type] = function ( id: string, definition: Function | Object ): Function | Object | void { //這裡的definition指的是定義(Function或Object),是函式或者物件 //如果definition不存在,直接返回options內type和id對應的//這裡的options是指全域性的元件,指令和過濾器,見圖一 if (!definition) { return this.options[type + 's'][id] } else { /* istanbul ignore if */ if (process.env.NODE_ENV !== 'production' && type === 'component') { validateComponentName(id) } // 如果是component(元件)方法,並且definition是物件 if (type === 'component' && isPlainObject(definition)) { definition.name = definition.name || id definition = this.options._base.extend(definition) } if (type === 'directive' && typeof definition === 'function') { definition = { bind: definition, update: definition } } // 將構造器賦值給 this.options[‘component’+ 's'][id] //全域性的元件,指令和過濾器,統一掛在vue.options上。在init的時候利用mergeOptions合併策略侵入例項,供例項使用。 this.options[type + 's'][id] = definition return definition } } }) }
initAssetRegisters裡面通過this.options._base.extend方法將定義物件轉化為構造器,而this.options._base.extend其實就是Vue.extend。接下來我們就看一下Vue.extend做了什麼。
Vue.extend = function (extendOptions) { extendOptions = extendOptions || {}; var Super = this; var SuperId = Super.cid; //元件快取 var cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {}); //如果元件已經被快取在extendOptions上則直接取出 if (cachedCtors[SuperId]) { return cachedCtors[SuperId] } //如果有name屬性,檢驗name拼寫是否合法 var name = extendOptions.name || Super.options.name; if ("development" !== 'production' && name) { validateComponentName(name); } var Sub = function VueComponent (options) { this._init(options); }; //將vue上原型的方法掛在Sub.prototype中,Sub的例項同時也繼承了vue.prototype上的所有屬性和方法。 //關於 prototype的學習:http://www.cnblogs.com/dolphinX/p/3286177.html Sub.prototype = Object.create(Super.prototype); //Sub建構函式修正,學習於https://www.cnblogs.com/SheilaSun/p/4397918.html Sub.prototype.constructor = Sub; Sub.cid = cid++; //通過vue的合併策略合併新增項到新的構造器上 Sub.options = mergeOptions( Super.options, extendOptions ); //快取父構造器 Sub['super'] = Super; // 處理props和computed響應式配置項 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; //在新的構造器上掛上vue的工具方法 ASSET_TYPES.forEach(function (type) { Sub[type] = Super[type]; }); // enable recursive self-lookup 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. Sub.superOptions = Super.options; Sub.extendOptions = extendOptions; Sub.sealedOptions = extend({}, Sub.options); //快取元件構造器在extendOptions上 cachedCtors[SuperId] = Sub; return Sub };
vue.extend返回了一個帶有附加Option的vue構造器。這個構造器被命名為Sub,等待render時候初始化。
initAssetRegisters完成之後,options下掛載了全域性元件button-counter,如圖: