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上。

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