Vue在注册组件时有两种方式,全局注册和局部注册
全局注册的话我们可以在任意组件中使用注册的组件,而局部注册的话我们只能在当前的组件中使用注册到的组件,
全局注册实例
Vue.component("app",App);
局部注册实例
components:{
App
}
下面我们进入两种注册方式的源码实现,首先进入Vue.component
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
}
}
这里是对三种方法的定义'component', 'directive', 'filter',这里我们要说的是component,可以看到,当type = 'component
时,直接调用了extend方法,extends 就是对Vue对象的扩展,就是一个组件的Vue构造函数,拥有Vue的所有方法,最后,将这个扩展赋值给了全局Vue 的options 的components 属性中,然后,我们进入到patch时 解析标签时的
let vnode, ns
if (typeof tag === 'string') {
let Ctor
ns = (context.$vnode && context.$vnode.ns) || config.getTagNamespace(tag)
if (config.isReservedTag(tag)) {
// platform built-in elements
vnode = new VNode(
config.parsePlatformTagName(tag), data, children,
undefined, undefined, context
)
} else if ((!data || !data.pre) && isDef(Ctor = resolveAsset(context.$options, 'components', tag))) {
// component
vnode = createComponent(Ctor, data, context, children, tag)
} else {
// unknown or unlisted namespaced elements
// check at runtime because it may get assigned a namespace when its
// parent normalizes children
vnode = new VNode(
tag, data, children,
undefined, undefined, context
)
}
} else {
// direct component options / constructor
vnode = createComponent(tag, data, context, children)
}
当标签类型为普通标签时,会创建普通类型的标签,如果不是普通标签时我们可以看else if里,Ctor = resolveAsset(context.$options, 'components', tag)
我们找到这个函数,定义在源码src/core/util/options.js
export function resolveAsset (
options: Object,
type: string,
id: string,
warnMissing?: boolean
): any {
/* istanbul ignore if */
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]
if (process.env.NODE_ENV !== 'production' && warnMissing && !res) {
warn(
'Failed to resolve ' + type.slice(0, -1) + ': ' + id,
options
)
}
return res
}
这里的options是 Vue构造函数的options和当前组件options的组合,这里是重点,,这个options是由之前调用的extends 方法完成合并的,,这也是全局组件和局部组件的区别所在,可以看到代码中是通过这个options 对象来找 注册的组件,三个if是对驼峰式和大写式查找的支持,因为之前的component方法定义中已经将这个全局注册的组件的名字放到了Vue构造函数的options中,所以在这里可以通过组合的options[组建名称]查找到对应的组件构造函数,因为这个options包含Vue构造函数options和本组件的options,所以在任何地方都可以使用全局注册的组件,而局部注册的组件只会在局部组件的配置合并中合并到局部options中,当在其他组件的options中寻找注册的组件时就会找不到,所以局部注册的组件只存在于局部options中,下面我们看下局部options的合并
export function initInternalComponent (vm: Component, options: InternalComponentOptions) {
const opts = vm.$options = Object.create(vm.constructor.options)
// doing this because it's faster than dynamic enumeration.
const parentVnode = options._parentVnode
opts.parent = options.parent
opts._parentVnode = parentVnode
const vnodeComponentOptions = parentVnode.componentOptions
opts.propsData = vnodeComponentOptions.propsData
opts._parentListeners = vnodeComponentOptions.listeners
opts._renderChildren = vnodeComponentOptions.children
opts._componentTag = vnodeComponentOptions.tag
if (options.render) {
opts.render = options.render
opts.staticRenderFns = options.staticRenderFns
}
}
这里是局部组件options 合并的代码,局部注册的components属性便会合并到options中,所以在当前templete中的component标签便可以在此组件的options中找到对应的构造函数从而构造出对应的组件vnode。
所以 options就是组件的构造函数的属性包括data啊,computed啊,props啊,component那些,因为组件树的底层options都会包含Vue构造函数options,所以,在Vue中注册的component可以在任何地方访问到,