Vue源码解析系列——组件篇:合并配置

准备

vue版本号2.6.12,为方便分析,选择了runtime+compiler版本。

回顾

如果有感兴趣的同学可以看看我之前的源码分析文章,这里呈上链接:《Vue源码分析系列:目录》

mergeOptions

通过前面的学习我们了解到,Vue对于非组件和组件有着不同的合并options的策略,具体体现在_init中:

//对options进行合并
    if (options && options._isComponent) {
      //对组件option的合并
      // optimize internal component instantiation
      // since dynamic options merging is pretty slow, and none of the
      // internal component options needs special treatment.
      initInternalComponent(vm, options);
    } else {
      //对普通的vm的options的合并
      vm.$options = mergeOptions(
        resolveConstructorOptions(vm.constructor),
        options || {},
        vm
      );
    }

对于组件,Vue使用了initInternalComponent进行合并配置。对于非组件,Vue使用了resolveConstructorOptions进行了一层处理后用mergeOptions进行配置合并。先忽略resolveConstructorOptions的逻辑,我们就把他的返回值当做是Vue默认的options,定义在global-api/index.js中

 Vue.options = Object.create(null)
 ASSET_TYPES.forEach(type => {
   Vue.options[type + 's'] = Object.create(null)
 })

好,我们继续看非组件的合并配置逻辑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);

  // Apply extends and mixins on the child options,
  // but only if it is a raw options object that isn't
  // the result of another mergeOptions call.
  // Only merged options has the _base property.
  if (!child._base) {
    if (child.extends) {
      parent = mergeOptions(parent, child.extends, 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;
}

一开始checkComponents检查组件的规范性。
然后是组件的normalize
接下来是一个判断,使用递归将extendsmixins合并到parent上。
遍历parent,调用mergeField
遍历child,如果key不在parent上,调用mergeField
mergeField的定义,很有意思,大致逻辑是使用不同的合并策略来处理不同的key。比如合并生命周期的策略:

function mergeHook(
  parentVal: ?Array<Function>,
  childVal: ?Function | ?Array<Function>
): ?Array<Function> {
  //如果不存在childVal,就返回parentVal
  //如果存在childVal且存在parentVal,就返回parentVal.concat(childVal)
  //如果存在childVal且不存在parentVal,就判断childVal是不是一个数组,如果是数组就返回childVal,如果不是数组就让childVal变成输入并返回
  const res = childVal
    ? parentVal
      ? parentVal.concat(childVal)
      : Array.isArray(childVal)
      ? childVal
      : [childVal]
    : parentVal;
  return res ? dedupeHooks(res) : res;
}

大致意思是将父和子的所有生命周期钩子合并为一个数组。
回到mergeOptions,最后返回合并完毕的options
接下来是组件vm的配置合并策略。
组件的vm是继承至Vue的,并且在继承的时候就已经merge过一次options了,具体代码定义在global-api/extend.js中:

const Sub = function VueComponent(options) {
      this._init(options);
    };
//es5继承
Sub.prototype = Object.create(Super.prototype);
Sub.prototype.constructor = Sub;
Sub.cid = cid++;
Sub.options = mergeOptions(Super.options, extendOptions);

可以看到在extend的过程中,已经调用了一次mergeOptions传入的是VueoptionsextendOptions组件的options,合并为Sub.options。之后实例化组件的时候还会调用一次_init,进入组件的合并逻辑initInternalComponent,进入initInternalComponent:

initInternalComponent

export function initInternalComponent(
  vm: Component,
  options: InternalComponentOptions
) {
  const opts = (vm.$options = Object.create(vm.constructor.options));
  // doing this because it's faster than dynamic enumeration.
  const parentVnode = options._parentVnode;
  opts.parent = options.parent; //activeInstance,当前vm的实例,也就是组件的父vm实例
  opts._parentVnode = parentVnode; //占位符vnode

  const vnodeComponentOptions = parentVnode.componentOptions;
  opts.propsData = vnodeComponentOptions.propsData;
  opts._parentListeners = vnodeComponentOptions.listeners;
  opts._renderChildren = vnodeComponentOptions.children;
  opts._componentTag = vnodeComponentOptions.tag;

  if (options.render) {
    opts.render = options.render;
    opts.staticRenderFns = options.staticRenderFns;
  }
}

创建了一个空对象,__proto__指向Sub.options并赋值给vm.$options,接下来就是一些赋值,就是将Sub.options中的整理至vm.$options中,也没有什么复杂的逻辑。
至此,Vue的配置合并逻辑解读完毕。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱学习的前端小黄

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值