共计 2646 个字符,预计需要花费 7 分钟才能阅读完成。
最近在 React 我的项目中用到了公司官网的图标库。以前在 Vue 我的项目外面应用图标库采纳全局注册的形式,这样组件外面能够间接应用图标组件,无需手动 import
而后再注册组件。然而当初的 React 没方法像 Vue 一样实现全局注册,只能手动 import
。所以我就很好奇,Vue 到底怎么实现全局注册的。
在之前的我的项目中,看到过有这种用法:
Vue.prototype.$axios = axios;
在下面代码中,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.component
,Vue.directive
和 Vue.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 官网文档