在Vue.js中,定义了一些内置组件(keepAlive、component、transition、transition-group、slot等),可以直接使用。除此之外,如果我们想使用一些自定义组件,必须要先注册组件,否则会报如下错误。
全局注册
使用Vue.component(tagName, options)注册一个全局组件。
export 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
// 用Vue.extend将definition转化为构造器
definition = this.options._base.extend(definition)
}
if (type === 'directive' && typeof definition === 'function') {
definition = { bind: definition, update: definition }
}
// 给Vue.options上扩展definition
this.options[type + 's'][id] = definition
return definition
}
}
})
以上是对Vue.options[components]的扩展,那组件到底是在什么地方注册的呢?
在_createElement中判断是我们自定义的标签(非预留标签)时,会调用resolveAsset()对构造器名称处理(比如小驼峰、大驼峰,这也是为什么我们可以使用多种方式命名组件),再调用createComponent生成vnode。
export function _createElement (
context: Component,
tag?: string | Class<Component> | Function | Object,
data?: VNodeData,
children?: any,
normalizationType?: number
): VNode | Array<VNode> {
if (typeof tag === 'string') {
let Ctor
if (config.isReservedTag(tag)) {
// platform built-in elements
} else if ((!data || !data.pre) && isDef(Ctor = resolveAsset(context.$options, 'components', tag))) {
// component
vnode = createComponent(Ctor, data, context, children, tag)
} else {
// ...
vnode = new VNode(tag, data, children, undefined, undefined, context)
}
}
}
export function resolveAsset (
options: Object,
type: string,
id: string,
warnMissing?: boolean
): any {
if (typeof id !== 'string') return;
const assets = options[type]
// check local registration variations first
if (hasOwn(assets, id)) return assets[id]
const camelizedId = camelize(id)
if (hasOwn(assets, camelizedId)) return assets[camelizedId]
const PascalCaseId = capitalize(camelizedId)
if (hasOwn(assets, PascalCaseId)) return assets[PascalCaseId]
// fallback to prototype chain
const res = assets[id] || assets[camelizedId] || assets[PascalCaseId]
return res
}
局部注册
局部注册是通过Vue.extend方法,代码Sub.options = mergeOptions(Super.options, extendOptions)将配置合并。随后在子组件初始化合并配置时,经过vm.$options = Object.create(vm.constructor.options)将其合并到$options上。