Vue.component|Vue.component 怎么实现全局注册的

最近在 React 项目中用到了公司官方的图标库。以前在 Vue 项目里面使用图标库采用全局注册的方式,这样组件里面可以直接使用图标组件,无需手动 import 然后再注册组件。但是现在的 React 没办法像 Vue 一样实现全局注册,只能手动 import 。所以我就很好奇,Vue 究竟怎么实现全局注册的。
在之前的项目中,看到过有这种用法:

Vue.prototype.$axios = axios;

【Vue.component|Vue.component 怎么实现全局注册的】在上面代码中,axios 被挂载到了 Vue 原型对象上面,这样在每一个组件实例中都可以通过 this.$axios 访问到 axois ,也就是实现了全局注册。
看源码之前,可以大胆猜测一下,组件全局注册应该是把组件挂载到了 Vue 构造器上面,这样创建的每一个 Vue 实例,里面的 $options 都包含了这个组件,因此就可以直接使用。
Vue.component 是怎么使用的 看源码之前,先看看官方文档的描述,这样更容易理解。
// {String} id // {Function | Object} [definition] Vue.component( id, [definition] )

这个 api 的作用是注册或获取全局组件。注册还会自动使用给定的 id 设置组件的名称。
// 注册组件,传入一个扩展过的构造器 Vue.component('my-component', Vue.extend({ /* ... */ }))// 注册组件,传入一个选项对象 (自动调用 Vue.extend) Vue.component('my-component', { /* ... */ })// 获取注册的组件 (始终返回构造器) var MyComponent = Vue.component('my-component')

源码解析 Vue.component 的源码在这个目录下:
node_modules\vue\src\core\global-api\assets.js
但是看源码之前,先看下 global-api\index.js 。这部分主要是初始化 Vue 构造函数,在上面挂载各种全局 api 。然后在第 54 行,有这样一段代码:
Vue.options = Object.create(null) ASSET_TYPES.forEach(type => { Vue.options[type + 's'] = Object.create(null) })

这里的作用是在 Vue 上面初始化一个 options 对象,然后对象的 key 来自于这里:
export const ASSET_TYPES = [ 'component', 'directive', 'filter' ]

这段代码执行完应该可以得到这个:
Vue.options = { components: {}, directives: {}, filters: {} }

然后接着在 global-api\index.js:68 有这样一段代码:
initAssetRegisters(Vue)

initAssetRegisters 函数就定义在 global-api\assets.js 里面,一起来看下:
import { ASSET_TYPES } from 'shared/constants' import { isPlainObject, validateComponentName } from '../util/index'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 } } }) }

从这里可以看出,Vue.componentVue.directiveVue.filter 都共用一套代码,但我们这里只关心 Vue.component
我们先来看函数的入参,按照文档的描述,Vue.component 第一个参数 id 是组件名,第二个参数 definition 可以是一个选项对象,或者一个构造器。
然后来看下这段代码:
if (type === 'component' && isPlainObject(definition)) { definition.name = definition.name || id definition = this.options._base.extend(definition) }

这段代码的意思就是,Vue.component 如果传入的是一个选项对象,那么就调用 extend 方法转为构造器,这与官方文档描述一致。
然后这段代码将构造器挂载到 Vue.options 上面:
this.options[type + 's'][id] = definition

挂载完成之后,应该会得到下面的结果:
Vue.options = { components: { /* Vue 一些内置组件,例如 keep-alive 也会挂载到这里 */ 'custom-component': VueComponent(options) }, directives: {}, filters: {} }

到这里答案应该很明确了,Vue.component 通过把自定义组件挂载到 Vue.options.components 里面,从而实现全局注册。
参考 Vue.component - Vue 官方文档

    推荐阅读