Vue2.0源码学习(6) - 组件注册

vue

Vue2.0源码学习(6) - 组件注册

组件注册

前言

在 Vue.js 中,除了它内置的组件如 keep-alive、component、transition、transition-group 等,其它用户自定义组件在使用前必须注册。在开发过程中可能会遇到如下报错信息:

Unknown custom element: <app> - did you register the component correctly? 

For recursive components, make sure to provide the "name" option.

一般报这个错的原因都是我们使用了未注册的组件。Vue.js 提供了 2 种组件的注册方式,全局注册和局部注册。接下来我们从源码分析的角度来分析这两种注册方式。

全局注册

在初始化加载阶段会调用initAssetRegisters函数把需要注册的组件挂载到Vue.options上。

// src\core\global-api\index.js

initAssetRegisters(Vue)

// src\core\global-api\assets.js

export function initAssetRegisters (Vue: GlobalAPI) {

// 标注①

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)) {

// 优先拿name,没有则取id

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

}

}

})

}

标注①:

对ASSET_TYPES进行遍历,我们先看看遍历对象ASSET_TYPES是什么?

// src\shared\constants.js

export const ASSET_TYPES = [

'component',

'directive',

'filter'

]

其实就是存放着插件、指令、过滤器这三个分类名称的数组,这里我们只单独针对component进行分析。
标注②:

this.options._base其实是Vue,具体原因请查看之前的文章《组件的创建和patch过程》。

通过Vue.extend把对象转换成构造器。

最后把definition放到this.options即Vue.options上,然后return definition。

虽然挂载到Vue.options上,但是又是什么时候会被拿去注册成真正的组件呢?

我们回顾_createElement函数:

// src\core\vdom\create-element.js

export function _createElement (

context: Component,

tag?: string | Class<Component> | Function | Object,

data?: VNodeData,

children?: any,

normalizationType?: number

): VNode | Array<VNode> {

...

if (typeof tag === 'string') {

//是否HTML原生标签

if (config.isReservedTag(tag)) {

...

// 标注①:resolveAsset

} else if ((!data || !data.pre) && isDef(Ctor = resolveAsset(context.$options, 'components', tag))) {

// component

vnode = createComponent(Ctor, data, context, children, tag)

} else {

...

}

}

}

标注①:resolveAsset函数做了什么?

// 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]

//判断配置中是否存在该组件

if (hasOwn(assets, id)) return assets[id]

const camelizedId = camelize(id)

//id转换成驼峰型判断

if (hasOwn(assets, camelizedId)) return assets[camelizedId]

const PascalCaseId = capitalize(camelizedId)

//id转换成首字母大写判断

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

}

其实就是经过各种情况判断识别Vue.options是否有定义该组件,有的话则返回,然后最后经过createComponent函数进行了组件的注册。

局部注册

局部注册其实和全局注册的几乎一样,只是它需要在此前做一个option合并:

// src\core\global-api\extend.js

Sub.options = mergeOptions(

Super.options,

extendOptions

)

关于合并的详细分析请查阅之前文章《合并配置》

由于合并配置是挂载于Sub上的,也就是说它只是一个在当前Sub作用域下的,一次这种创建方式的组件只能局部使用。

以上是 Vue2.0源码学习(6) - 组件注册 的全部内容, 来源链接: utcz.com/z/380069.html

回到顶部