Vue学习(一):Vue中的选项合并策略

开端

最近为了给自己充电,在腾讯课堂报了一个vue源码学习的课程,跟着课堂上的老师学习vue源码以此来增加自己对于vue的理解。这篇文章主要是记录一下自己学习vue源码时,学习到了什么,并把自己对vue源码的理解通过语音的形式给描述出来。这里并不会一字一句的解释vue中的每一句源码的意思,因为有些我自己还不解其意,但不妨碍从中学习到知识。

vue版本:2.5.1
源码范围

1.实例化vue时参数的合并策略以及规范。
2.vue通过Vue.extend方法进行组件参数扩展时的参数合并策略。
3.vue的mixins字段的参数合并方式。


使用vue创建一个实例时,传递给Vue的构造函数的参数会变成什么?

var vm = new Vue({
    el: '#app',
    data: {
        message: 'hello world!',
    },
});

这里我们不探究new Vue时,其他地方发生了什么,本篇文章我们只关心vue源码中是怎么处理Vue构造函数接收过来的参数的。我们不妨通过控制台打印一下 vm.$options 看一下是什么:
 

vm.$options对象的输出结果
vm.$options对象的输出结果标题

这$options属性就是vue实例存储的合并Vue参数后的对象,有el,components组件,data数据,directives指令等。首先我们上面的例子中并没有传递components和directives这两个参数,可实例中却有值,然后data却是一个函数,这又是为什么呢?vue中他究竟是怎么合并这些参数的呢?下面我们就从简单到复杂慢慢来学习一下vue的源码中究竟做了什么事吧。

Vue的初始化函数 —— _init函数

首先,Vue对象是一个构造函数,我们先隐匿掉其他部分,只留下和参数合并相关的代码,以便让我们进行关注点分离,更容易学习,精简后的Vue构造函数长这样:

function Vue (options) {
    this._init(options);
}

哦豁,真的是简单的不能再简单了,其实也确实如此,_init函数就是Vue实例化的入口,我们来找到_init函数,看他又做了什么:

Vue.prototype._init = function (options) {
  var vm = this;
  vm.$options = mergeOptions(
    resolveConstructorOptions(vm.constructor),
    options || {},
    vm
  );
};

上面的代码看起来只有一个作用,就是通过mergeOptions函数合并vm实例的参数,当然整个_init方法远不止这么简单,但我们的目的很明确:学习vue中的对于参数的合并处理是怎么做的,其他vue中对于一些其他操作我们可以忽略,事实上我也确实是把_init方法给删的只有一个功能了。你此时只需要关系vue的参数合并功能即可。这也是这篇文章的目的。

mergeOptions方法

重点在于这个mergeOptions方法,他的作用就是合并用户传递的参数的,看这一句:

mergeOptions(
    resolveConstructorOptions(vm.constructor),
    options || {},
    vm
)

我们给mergeOptions方法传递了三个参数,第二个第三个好理解,一个就是用户传递参数,没传则为{}对象,vm就是当前new Vue时创建的那个实例,而这一句是什么? resolveConstructorOptions(vm.constructor) ,我们知道Vue中是有默认参数的,因为上一个例子中我们并没有向Vue构造函数中传递components参数,可vm实例中却还是有一个components属性,说明这是vue给他的默认值,那vue的默认值在哪呢?由于此处vue中的源码不是那么简单就可以拿出来的,所以省略为如下代码:

Vue.options = {
    components: {},
    directives: {},
    _base: Vue,
};

也就是说vue的默认参数是创建在Vue这个构造函数中的,当然还有其他的,只是简化成这样,实际要复杂的多,不过正如我之前所说的:我们的关注点不在这里,则隐藏它对于我们来说就很有必要了。
那么 resolveConstructorOptions(vm.constructor) 其实就是合并构造函数中的默认参数的,此处你可以直接变成这样:

mergeOptions(
    vm.constructor,
    options || {},
    vm
)

那么mergeOptions的作用就很明显了,合并Vue中的默认参数和用户传递过来的参数。

ps:为什么 resolveConstructorOptions 方法可以忽略?好吧,因为那个课堂老师没讲,然后我自己去看了一下也没看太懂,应该是合并父子类构造函数的默认参数的。没事,问题不大。

那mergeOptions方法内部究竟做了什么呢?我们来看一下:

  function mergeOptions (parent, child, vm) {

    checkComponents(child);

    normalizeProps(child, vm);
    normalizeInject(child, vm);
    normalizeDirectives(child);

    if (child.extends) {
      parent = mergeOptions(parent, child.extends, vm);
    }
    if (child.mixins) {
      for (var i = 0, l = child.mixins.length; i < l; i++) {
        parent = mergeOptions(parent, child.mixins[i], vm);
      }
    }

    var options = {};
    var key;
    for (key in parent) {
      mergeField(key);
    }
    for (key in child) {
      if (!hasOwn(parent, key)) {
        mergeField(key);
      }
    }
    function mergeField (key) {
      var strat = strats[key] || defaultStrat;
      options[key] = strat(parent[key], child[key], vm, key);
    }
    return options
  }

这个mergeOptions做的事不少,大致有以下几件:

1.checkComponents函数,校验child对象中组件参数components中的组件名是否符合规范
2.normalizeXXX函数,用来统一参数的形式,比如props支持数组和对象的写法,normalizeXXX函数的作用就是将他们统一转换为同一种形式,以便后续使用。
3.extends和mixins混入对象和合并,如果他们有extends或者mixins字段,则先进行遍历递归合并他们。
4.将child对象,扩展到parent对象上。

这里面,child和parent都是参数对象,而且,child的优先级要搞,也就是如果出现相同的属性参数,则以child中的为准,记住这个设定。后面的其他函数的设置都是后面的参数覆盖前面参数。

那么我们就是来讲讲他们的作用吧。

normalizeXXX函数

首先,checkComponents这个函数就不用讲了,校验组件名是否合法的,各位自行查看源码即可。现在看来一下类似的normalizeXXX函数做了什么事吧,我们拿normalizeDirectives做参考即可,这是代表指令的参数:

   function normalizeDirectives (options) {
      var dirs = options.directives;
      if (dirs) {
        for (var key in dirs) {
          var def = dirs[key];
          if (typeof def === 'function') {
            dirs[key] = { bind: def, update: def };
          }
        }
      }
    }

代码不多,首先我们都知道,我们注册局部时,directives参数是一个对象,里面每一个指令属性值既可以接受一个函数,也可以指定一个对象用于设置指令的钩子函数。如果指令属性的值是一个函数,则它是同时作为bind和updata钩子函数进行绑定的&

  • 12
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值