Vue源码解析系列——组件篇:createComponent的执行过程

准备

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

回顾

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

createComponent的定义

createComponent定义在croe/vdom/create-component.js中:

export function createComponent(
  Ctor: Class<Component> | Function | Object | void,
  data: ?VNodeData,
  context: Component,
  children: ?Array<VNode>,
  tag?: string
): VNode | Array<VNode> | void {
  if (isUndef(Ctor)) {
    return;
  }

  //此处的_base指向Vue
  //_base定义在'global-api/index.js'中,指向Vue
  //因为在'core/instance/init.js'中已经进行了options的合并,故此处可以直接使用context.$options._base访问到Vue
  const baseCtor = context.$options._base;

  // plain options object: turn it into a constructor
  if (isObject(Ctor)) {
    //获取组件构造器,使之拥有与Vue相同的功能
    Ctor = baseCtor.extend(Ctor);
  }

  // if at this stage it's not a constructor or an async component factory,
  // reject.
  if (typeof Ctor !== "function") {
    if (process.env.NODE_ENV !== "production") {
      warn(`Invalid Component definition: ${String(Ctor)}`, context);
    }
    return;
  }

  // async component
  let asyncFactory;
  if (isUndef(Ctor.cid)) {
    asyncFactory = Ctor;
    Ctor = resolveAsyncComponent(asyncFactory, baseCtor);
    if (Ctor === undefined) {
      // return a placeholder node for async component, which is rendered
      // as a comment node but preserves all the raw information for the node.
      // the information will be used for async server-rendering and hydration.
      return createAsyncPlaceholder(asyncFactory, data, context, children, tag);
    }
  }

  data = data || {};

  // resolve constructor options in case global mixins are applied after
  // component constructor creation
  resolveConstructorOptions(Ctor);

  // transform component v-model data into props & events
  if (isDef(data.model)) {
    transformModel(Ctor.options, data);
  }

  // extract props
  const propsData = extractPropsFromVNodeData(data, Ctor, tag);

  // functional component
  if (isTrue(Ctor.options.functional)) {
    return createFunctionalComponent(Ctor, propsData, data, context, children);
  }

  // extract listeners, since these needs to be treated as
  // child component listeners instead of DOM listeners
  const listeners = data.on;
  // replace with listeners with .native modifier
  // so it gets processed during parent component patch.
  data.on = data.nativeOn;

  if (isTrue(Ctor.options.abstract)) {
    // abstract components do not keep anything
    // other than props & listeners & slot

    // work around flow
    const slot = data.slot;
    data = {};
    if (slot) {
      data.slot = slot;
    }
  }

  // install component management hooks onto the placeholder node
  //安装组件的钩子
  installComponentHooks(data);

  // return a placeholder vnode
  const name = Ctor.options.name || tag;
  //生成一个vnode
  const vnode = new VNode(
    `vue-component-${Ctor.cid}${name ? `-${name}` : ""}`,
    data,
    undefined,
    undefined,
    undefined,
    context,
    { Ctor, propsData, listeners, tag, children },
    asyncFactory
  );

  // Weex specific: invoke recycle-list optimized @render function for
  // extracting cell-slot template.
  // https://github.com/Hanks10100/weex-native-directive/tree/master/component
  /* istanbul ignore if */
  if (__WEEX__ && isRecyclableComponent(vnode)) {
    return renderRecyclableComponentTemplate(vnode);
  }

  return vnode;
}

函数体有些长,我们一行一行来看,先看第一行:

const baseCtor = context.$options._base;

此处将上下文对象中的$options中的_base赋值给baseCtor,那_base定义在哪里?_base定义在global-api/index.js中,我们找到了这样的一行定义:

Vue.options._base = Vue

由此可以得知,Vue.options._base指向的是Vue类,那为何在上下文对象的$options中也可以访问到这个_base呢?还记得在_init中有这样的一段代码吗?

if (options && options._isComponent) {
      // 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 = mergeOptions(
      resolveConstructorOptions(vm.constructor),
      options || {},
      vm
    );
  }

这里对options进行了合并,所以在createComponent中可以直接使用context.$options._base访问到Vue类。
回到createComponent,继续往下看:

if (isObject(Ctor)) {
  Ctor = baseCtor.extend(Ctor);
}
  // if at this stage it's not a constructor or an async component factory,
  // reject.
if (typeof Ctor !== "function") {
  if (process.env.NODE_ENV !== "production") {
    warn(`Invalid Component definition: ${String(Ctor)}`, context);
  }
  return;
}

如果Ctor是一个对象,就执行extend,如果不是一个函数就抛出警告。

Vue.extend

那着个extend是什么呢,我们去源码里寻找一下这个extendapi,定义在global-api/extend.js中:

Vue.extend = function (extendOptions: Object): Function {
    extendOptions = extendOptions || {};
    const Super = this;
    const SuperId = Super.cid;
    //获取构造器缓存
    const cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {});
    //如果缓存中有构造器就直接取出并返回
    if (cachedCtors[SuperId]) {
      return cachedCtors[SuperId];
    }

定义Super指向Vue类,获取缓存,如果缓存中有相应的结果,直接返回。

const name = extendOptions.name || Super.options.name;
if (process.env.NODE_ENV !== "production" && name) {
   //校验组件名称
   validateComponentName(name);
}

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);
Sub["super"] = Super;

ES5的继承方法,使被继承的原型指向父类,构造器指回自身

//缓存构造器
cachedCtors[SuperId] = Sub;
return Sub;

将构造器缓存并返回。
所以Ctor = baseCtor.extend(Ctor);这里的意思是获取组件的构造器,使之与Vue构造器拥有相同的功能。
回到createComponent,继续往下看,略过一些非主线代码:

installComponentHooks(data);

// return a placeholder vnode
const name = Ctor.options.name || tag;
//生成一个vnode
const vnode = new VNode(
  `vue-component-${Ctor.cid}${name ? `-${name}` : ""}`,
  data,
  undefined,
  undefined,
  undefined,
  context,
  { Ctor, propsData, listeners, tag, children },
  asyncFactory
);

if (__WEEX__ && isRecyclableComponent(vnode)) {
 return renderRecyclableComponentTemplate(vnode);
}
return vnode;

安装了组件的hooks,生成了一个VNode,注意此处的VNode的children是undefined,但是在第六个参数options中又传入了children,这是和普通VNode不同的地方,最后返回了这个VNode。
至此,createComponent阅读完毕。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

爱学习的前端小黄

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

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

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

打赏作者

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

抵扣说明:

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

余额充值