Vue源码解析系列——组件篇:组件注册

准备

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

回顾

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

组件注册

Vue的组件祖册分为两部分,一部分为全局组件注册,也就是使用Vue.component(tag,options),另外一部分为局部组件注册,使用options.component = {App}
我们先来看全局组件注册。

import HelloWorld from './components/HelloWorld';

Vue.component('HelloWorld', HelloWorld);

new Vue({
  render: h => h(App)
}).$mount('#app');

这里使用了Vue.component注册了一个HelloWorld全局组件。
首先看Vue.component

Vue.component

Vue.component定义在global-api/assets.js中:

export function initAssetRegisters (Vue: GlobalAPI) {
 /**
  * Create asset registration methods.
  */
 ASSET_TYPES.forEach(type => {
   Vue[type] = function (
     id: string,
     definition: Function | Object
   ): Function | Object | void {
     if (!definition) {
       return this.options[type + 's'][id]
     } else {
       /* istanbul ignore if */
       if (process.env.NODE_ENV !== 'production' && type === 'component') {
         validateComponentName(id)
       }
       if (type === 'component' && isPlainObject(definition)) {
         definition.name = definition.name || id
         definition = this.options._base.extend(definition)
       }
       if (type === 'directive' && typeof definition === 'function') {
         definition = { bind: definition, update: definition }
       }
       this.options[type + 's'][id] = definition
       return definition
     }
   }
 })
}

首先遍历ASSET_TYPES

export const ASSET_TYPES = [
 'component',
 'directive',
 'filter'
]

继续向下看initAssetRegisters:

if (type === 'component' && isPlainObject(definition)) {
         definition.name = definition.name || id
         definition = this.options._base.extend(definition)
       }

typecomponentdefinition为一个对象的时候,执行Vue.extend,传入componentoptions
之前我们介绍过Vue.extend,这个方法里面会执行一个mergeOptions

Sub.options = mergeOptions(Super.options, extendOptions);

将Vue内置的options与刚刚传入的组件的options合并,并赋值在组件构造器的options属性内。
所以最终definition拿到的是一个合并options后的组件的构造器。
继续往下看:

 this.options[type + 's'][id] = definition;
 return definition

definition放到Vue的components.id上,然后返回definition
也就是说,Vue.component("HelloWorld",HelloWorld);之后,Vue的components.HelloWorld就是一个HelloWorld组件的构造器了。
之后运行new Vue,进入_initmergeOptions进行配置合并。这里的components的合并策略和生命周期函数的合并策略并不一样:

function mergeAssets(
 parentVal: ?Object,
 childVal: ?Object,
 vm?: Component,
 key: string
): Object {
 const res = Object.create(parentVal || null);
 if (childVal) {
   process.env.NODE_ENV !== "production" &&
     assertObjectType(key, childVal, vm);
   return extend(res, childVal);
 } else {
   return res;
 }
}

这里是将Vue.components合并在了options.components__proto__上。
再往下运行就是进入_render,然后再进入_createElement,其中_createElement中有一个判断:

 else if (
     (!data || !data.pre) &&
     isDef((Ctor = resolveAsset(context.$options, "components", tag)))
   ) {
     //创建一个组件vnode
     // component
     vnode = createComponent(Ctor, data, context, children, tag);
   }

会去寻找vm.$options.components.tag对应的组件构造器,如果找到了就将构造器传入createComponent,生成一个组件vnode用于之后的_patch_
到这里,全局组件的注册过程就解析完毕了。

局部组件的注册

其实了解了全局组件的注册过程后,局部组件的注册过程理解起来就更加简单了。其实局部组件注册只是执行了刚刚提到的_init中的mergeOptions,将子组件的optionsmerge到父组件的vm.$options上。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

爱学习的前端小黄

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

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

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

打赏作者

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

抵扣说明:

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

余额充值