本文已参与「新人创作礼」活动,一起开启掘金创作之路。
1.Vue组件注册
我们知道vue中注册组件的方式有两种,一种是全局注册,另一种是局部注册,注册方式如下:
全局注册
const HelloWorld = {
name: xxx,
data() {
return {}
}
...
}
Vue.component('HelloWorld', Helloworld)
局部注册
import HelloWorld from 'xxx/xxx'
export default {
components: {
HelloWorld
}
}
那么为什么全局注册的组件所有地方都能用到?在注册组件过程中又发生了什么呢?接下来我们根据源码来分析!
2.组件全局注册
Vue.component
话不多说,上源码:
Vue.component初始化定义在src/core/global-api/assets目录下:
/* @flow */
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) {
// 不传入第二个对象,直接返回存在Vue上的对应方法
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') {
// 如果是detective
definition = { bind: definition, update: definition }
}
// 普通component的情况下,在Vue.options.components[id]中存储构造器
this.options[type + 's'][id] = definition
// 若为component,返回构造器
return definition
}
}
})
}
export const ASSET_TYPES = [
'component',
'directive',
'filter'
]
initAssetRegisters这个函数是在initClobalAPI的函数中执行的,这个是在Vue初始化的过程中执行的。
可以看到这个函数遍历了ASSET_TYPES,在Vue的构造函数上注册了(component、directive、filter)这些方法。
这些注册的方法直接传入一个参数的时候,可以获取已经生成的对应方法或对象。
例Vue.component('HelloWorld')
,这样执行返回的是一个已经注册过的子类构造器
当我们传入两个参数,也就是通过Vue.component('HelloWorld', Helloworld)
调用这个方法的时候,接下来会用validateComponentName校验组件名称:
export function validateComponentName (name: string) {
if (!/^[a-zA-Z][\w-]*$/.test(name)) {
warn(
'Invalid component name: "' + name + '". Component names ' +
'can only contain alphanumeric characters and the hyphen, ' +
'and must start with a letter.'
// 无效的组件名称:“' + name + '”。组件名称只能包含字母数字字符和连字符,而且必须以字母开头。”
)
}
if (isBuiltInTag(name) || config.isReservedTag(name)) {
warn(
'Do not use built-in or reserved HTML elements as component ' +
'id: ' + name
// 不要使用内置或保留的HTML元素作为组件的id
)
}
}
之后主要核心就是使用Vue.extend函数生成了构造器(this.options._base
就是Vue,也是在initGlobalAPI过程中,如下代码),Vue.extend的逻辑可以点击这里;
export function initGlobalAPI (Vue: GlobalAPI) {
......
Vue.options = Object.create(null)
ASSET_TYPES.forEach(type => {
Vue.options[type + 's'] = Object.create(null)
})
// this is used to identify the "base" constructor to extend all plain-object
// components with in Weex's multi-instance scenarios.
Vue.options._base = Vue
......
initAssetRegisters(Vue)
}
Vue.extend中有一段逻辑
Sub.options = mergeOptions(
Super.options,
extendOptions
)
mergeOptions可以参考mergeOptions。
这里也就是将传入的配置对象与Vue的options进行了合并,这样子组件构造器的options里面就有了我们注册的component(这就是为什么这样注册的组件能在全局使用)。
到这里,Vue.component这个函数我们已经清楚是做了什么事情了,也就是在Vue.options.components属性上存储了一个子类构造器,并将这个options与子类的options合并在一起存储在了子类构造器上;那第二个问题来了,这个子类构造器在页面渲染时是如何使用的呢?
我们继续往下分析,点击这里前往下一节