持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第10天,点击查看活动详情
前言
大家好,上一篇文章mixin的原理中我们对vue2中的mixin进行了源码解读,通过学习我们掌握了mixin是如何运行的以及它又是如何实现对象合并的。今天我们继续来分享Vue中另一个全局API - component。相信用过vue2的小伙伴对这个api并不陌生,相对于前几个全局API来说,component应该算是比较常用的了。关于该API的作用如果用一句话来概括的话就是:
用于注册或获取全局组件
下面我们先来看一下它的用法,然后再去解析一下它的源码。
案例展示
在日常vue开发中,每个.vue文件其实就是一个组件,如果我们想在某个组件a中使用另一个组件b,那么必须在a中通过import的先将b组件导入并在components中注册,然后才能在a中使用b。这种方式使用的是局部组件注册的实现的,也就是说哪里用到就在哪里注册。然而还有一种场景就是:某个组件是一个通用组件,可能需要在每个组件中都会用到,那么如果还使用局部注册每次都导入的话就太麻烦了。因此这个时候我们就可以利用Vue.component进行全局注册,一次注册终身受益。
我们还是以上篇文章中的a/b/c组件为例,现由于业务需要,要让每个组件中都显示一下项目的标题,那么这个时候我们就可以通过vue.component注册一个全局组件header,然后再在每个组件中直接使用即可实现了。
- main.js
Vue.component('myheader',{
template:'<h1>西瓜watermelon</h1>'
})
// 或者新建一个myheader.vue,把<h1>西瓜watermelon</h1>放在myheader.vue中
// 然后通过import导入myheader.vue,并用如下的方式注册
// Vue.component('myheader',myheader)
- a.vue/b.vue/c.vue
<div>
<myheader />
<div>{{msg}}</div>
</div>
源码解析
通过上面比较啰嗦的赘述终于要进入我们的正题了。下面我们就一起来看下源码吧(src/core/global-api/assets.js
)
const ASSET_TYPES = ['component', 'directive', 'filter']
ASSET_TYPES.forEach(type => {
Vue[type] = function (
id: string,
definition: Function | Object
): Function | Object | void {
if (!definition) {
return this.options[type + 's'][id]
} else {
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.component、Vue.directive和Vue.filter的源码,因为这三个方法的方式都差不多并且也不是很复杂,所以就放在一起做了。我们还是以component为例来解读一下:
- 首先是导入src/shared/constants.js,这里定义了一个ASSET_TYPES数组,数组的内容就是['component', 'directive', 'filter']
- 然后遍历该数组依次给Vue添加三个对应的静态方法component、directive和filter
- 该方法接收两个参数:string类型id和函数或对象类型的definition
- 在该函数体内首先判断definition是否存在,如果不存在则认为是获取而不是注册组件,则直接将组件返回
- 这里的this就是Vue本身,在Vue上有个options属性,而像components、directives等这些都会挂在Vue的options上,而所有的组件又都挂在了components上,因此通过this.options.components[id]就可以获得对应的组件
- 如果definition存在(else分支),则进行下一步判断,也就是对component和directive进行单独处理
- 如果type值是一个component并且definition是一个纯对象
- 则看definition是否有name属性,如果有直接使用,如果没有则用传进来的id作为definition的name
- 然后通过Vue.extend函数对definition进行扩展并将返回值重新赋值给definition(在前面我们已经分享过关于extend这个api了,其实就是通过extend扩展了一个Vue的子类。那么在这里也就是说将definition通过extend变成Vue的子类然后重新赋值给definition)
- 在Vue的options上还有个_base属性,而这个_base其实指向的也是Vue,这里没明白为啥不直接用this.extend而是要通过_base绕一下
- 最后再通过
this.options[type + 's'][id] = definition
将definition挂到Vue的options的components上,翻译过来就是:Vue.options.components[id] = definition 这样就完成全局组件的注册了
总结
如果将Vue通过console.dir(Vue)打印出来,你会发现:
- 所有的全局组件都是挂在这个Vue.options.components上的,其实我们也可以直接用Vue.options.components[组件名] = 组件的形式也能进行全局注册
- 在调用Vue.component时,如果第二个参数传递的是一个纯对象,那么在源码中会通过Vue.extend将对象扩展成Vue的子类然后再挂载到Vue的options的components上,那也就是说我们在使用Vue.component时第二个参数可以直接传递一个Vue.extend也能实现同样的效果