1. 程式人生 > 實用技巧 >【Vue】Vue原始碼解讀之Component元件註冊

【Vue】Vue原始碼解讀之Component元件註冊

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)

function
initAssetRegisters (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,如圖: