VUE源码分析之注册全局组件过程

      介绍Vue全局注册组件过程之前先大体认识一下Vue对象。这个对象的重要性不言而喻了。其实这个对象是个构造函数对象  

function Vue(options){

     this._init(options);

}

在new Vue实例的时候会执行_init函数进行一些初始化操作。而这个_init函数是Vue对象原型上的方法,是执行完全局函数initMixin函数后加上的。另外还会执行其他一些全局函数为Vue对象原型加上其他方法。这些全局函数为:

initMixin(Vue), stateMixin(Vue), eventsMixin(Vue), lifecycleMixin(Vue), renderMixin(Vue) 

这些是全局函数,加载Vue.js 时就要执行,所以Vue对象原型上有好多方法。例如:

Vue.prototype._init,  Vue.prototype.$set, Vue.prototype.$delete, Vue.prototype.$watch, Vue.prototype.$on, Vue.prototype.$once

Vue.prototype.$off, Vue.prototype._update, Vue.prototype._render

 

言归正传,介绍Vue全局组件注册过程。我们知道全局注册组件用法如下:

Vue.component("comp-name" {

      template: "<div>abc</div>",

      data: function(){

              return {

                     data1: "global component data"

              }

      },

      methods: {

               func1: function(){

                       console.log("global component func1");

               }

      }

})

从用法我们看出component 是Vue 上的一个函数,猜测是Vue对象上的方法,或是其原型上的方法。但在Vue.js中没有搜索到此方法。嗯,那怎么办呢,百度一下呗。

原来是在initGlobalAPI 这个全局方法搞的鬼, 接下来就解开这个全局方法真面目。这个函数最后调用了initAssetRegisters方法,进这个函数看下:

ASSET_TYPES.forEach(function (type) {
      Vue[type] = function (
        id,
        definition
      ) {
        if (!definition) {
          return this.options[type + 's'][id]
        } else {
          /* istanbul ignore if */
          if (type === 'component') {
            validateComponentName(id);
          }
          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[type + 's'][id] = definition;
          return definition
        }
      };
    });

ASSET_TYPES 是什么呢? 

var ASSET_TYPES = [
    'component',
    'directive',
    'filter'
  ]

嗯,Vue.component函数找到了。如果你没有找到,那你再找找。 我们来看看这个Vue.component函数体,看它到底干的什么,能注册组件。

首先看这个函数的可接收的参数,从用法上也能看出,第一个是字符串,第二个是对象。从 definition.name = definition.name || id;  这一行代码可以看到如果第二个对象没有name属性,就用第一个参数来做这个组件的名字。这个函数体的关键代码在这一行

definition = this.options._base.extend(definition);  通过这一行返回一个以Vue对象原型为原型的构造函数对象。 那得好好看看这个extend函数。

在initGlobalAPI全局函数中有这么一行Vue.options._base = Vue;    所以能看出extend方法其实是Vue对象上的方法。这个extend方法又在哪里呢?  我们来看看这一行代码initExtend(Vue);   

function initExtend (Vue) {
    /**
     * Each instance constructor, including Vue, has a unique
     * cid. This enables us to create wrapped "child
     * constructors" for prototypal inheritance and cache them.
     */
    Vue.cid = 0;
    var cid = 1;

    /**
     * Class inheritance
     */
    Vue.extend = function (extendOptions) {
      extendOptions = extendOptions || {};
      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 (name) {
        validateComponentName(name);
      }

      var Sub = function VueComponent (options) {
        this._init(options);
      };
      Sub.prototype = Object.create(Super.prototype);
      Sub.prototype.constructor = Sub;
      Sub.cid = cid++;
      Sub.options = mergeOptions(
        Super.options,
        extendOptions
      );
      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
      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);

      // cache constructor
      cachedCtors[SuperId] = Sub;
      return Sub
    };
  }

 

extend方法在这里。 下面要剖析一下这个函数看看怎么生成以Vue对象原型为原型的构造函数对象的?

var Sub = function VueComponent (options) {
        this._init(options);
      };
      Sub.prototype = Object.create(Super.prototype);
      Sub.prototype.constructor = Sub;

通过这几行代码可以看出这是原型继承。定义构造函数,使构造函数的原型指向到Vue对象的原型上。那么构造函数就具有了和Vue对象一样的原型。当然Vue对象的原型中的各种方法也都具备了。

到这里我们可以看到调用Vue.component方法来注册全局组件其实是生成了一个具备组件属性的Vue对象。其实还没有注册,注册过程在哪呢?看initAssetRegisters 这个方法,注意这一行代码:this.options[type + 's'][id] = definition;    这一行代码就完成注册了。怎么验证已经注册成功呢? 上demo

Vue.component("childComp", {

     template: "<div>abc</div>",

     data: function(){

          return{

                 data1: "hello, i am child component"

          }

     },

     props: ["parentMsg"],

     methods: {

            func1 : function(){

            }

     }

});

for(var key in Vue.options.components){

      console.log("register comps are: " + key);

}

你会看到打印:

register comps are: KeepAlive

register comps are: Transition

register comps are: TransitionGroup

register comps are: childComp

 

为什么还有KeepAlive  Transition  TransitionGroup 呢,就不多说了。

 

至此我们已经把注册全局组件过程讲清楚了,你还没看懂,就多看几遍,如果还有哪里没有明白的地方,可以留言,一块探讨一下。注册完之后就要用,怎么用呢?且听下回分解。

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值