人人都能懂的Vue源码系列(四)—mergeOptions

上篇文章中我们讲了resolveConstructorOptions,它的主要功能是解析当前实例构造函数上的options,不太明白的同学们可以看本系列的前几篇文章。在解析完构造函数上的options之后,需要把构造函数上的options和实例化时传入的options进行合并操作,并生成一个新的options。

这个合并操作就是今天要讲的mergeOptions。如果大家不想看枯燥的讲解,可以翻到文章最后,查看整个mergeOptions的流程图。

Merge two option objects into a new one. Core utility used in both instantiation and inheritance.

先来看源码中对mergeOptions方法的注释。mergeOptions的功能是合并两个options对象,并生成一个新的对象,是实例化和继承中使用的核心方法。既然这么重要,那我就带大家一行一行的来解析代码,让大家看完这篇文章后,能基本上理解mergeOptions。

export function mergeOptions (
  parent: Object,
  child: Object,
  vm?: Component
): Object {
  if (process.env.NODE_ENV !== 'production') {
    checkComponents(child) // 检查组件名称是否合法
  }

  if (typeof child === 'function') {
    child = child.options
  }

  normalizeProps(child, vm)
  normalizeInject(child, vm)
  normalizeDirectives(child)
  const extendsFrom = child.extends
  if (extendsFrom) {
    parent = mergeOptions(parent, extendsFrom, vm)
  }
  if (child.mixins) {
    for (let i = 0, l = child.mixins.length; i < l; i++) {
      parent = mergeOptions(parent, child.mixins[i], vm)
    }
  }
  const options = {}
  let key
  for (key in parent) {
    mergeField(key)
  }
  for (key in child) {
    if (!hasOwn(parent, key)) {
      mergeField(key)
    }
  }
  function mergeField (key) {
    const strat = strats[key] || defaultStrat
    options[key] = strat(parent[key], child[key], vm, key)
  }
  return options
}
复制代码

首先看传入的三个参数,parent,child,vm,这三个参数分别代表的是该实例构造函数上的options,实例化时传入的options,vm实例本身。结合Vue作者写的注释,我们明白了,原来mergeoptions方法是要合并构造函数和传入的options这两个对象。接下来往下看

if (process.env.NODE_ENV !== 'production') {
    checkComponents(child) // 检查组件名称是否合法
 }
复制代码

这段代码主要是判断当前环境是不是生产环境,如果不是,则调用checkComponents方法来检查组件名称是否是可用名称?我们来看看checkComponents的逻辑

function checkComponents (options: Object) {
  for (const key in options.components) {
    validateComponentName(key)
  }
}
export function validateComponentName (name: string) {
  if (!/^[a-zA-Z][\w-]*$/.test(name)) {
    warn(
      'Invalid component name: "' + name + '". Component names ' +
      'can only contain alphanumeric characters and the hyphen, ' +
      'and must start with a letter.'
    )
  }
  if (isBuiltInTag(name) || config.isReservedTag(name)) {
    warn(
      'Do not use built-in or reserved HTML elements as component ' +
      'id: ' + name
    )
  }
}
复制代码

如果child的options(实例化传入的options)有components属性,如下面这种情况

const app = new Vue({
   el: '#app',
   ...
   components: {
     childComponent
   }
   ...
})
复制代码

那么就调用validateComponentName来验证传入的组件名称是否符合以下特征

  1. 包含数字,字母,下划线,连接符,并且以字母开头
  2. 是否和html标签名称或svg标签名称相同
  3. 是否和关键字名称相同,如undefined, infinity等

如果满足第一条,并且第2,3条都是不相同的话,那么组件名称可用。 我们再回到mergeOptions源码中

if (typeof child === 'function') {
    child = child.options
}
复制代码

如果child是function类型的话,取其options属性作为child。 接下来看这三个以normalize开头的方法

normalizeProps(child, vm)
normalizeInject(child, vm)
normalizeDirectives(child)
复制代码

这三个方法的功能类似,分别是把options中props,inject,directives属性转换成对象的形式。有时候传入的是数组,如下面这种情况

Vue.component('blog-post', {
  props: ['postTitle'],
  template: 
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值