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的配置合并逻辑解读完毕。

你可能感兴趣的:(vue,源码)