Vue source code learning-componentization (5) component registration
学习内容和文章内容来自 黄轶老师
黄轶老师的慕课网视频教程地址:
"Vue.js2.0 Source Code Revealed" ,
黄轶老师拉钩教育教程地址:
"Vue.js 3.0 Core Source Code Analysis"
这里分析的源码是Runtime + Compiler 的 Vue.js
调试代码在:node_modules\vue\dist\vue.esm.js 里添加
vue版本:Vue.js 2.5.17-beta
你越是认真生活,你的生活就会越美好
——Frank Lloyd Wright
"The Fruit of Life" Classic Quotations
Click to go back to the complete list of Vue source code learning
Component registration
In Vue.js, in addition to its built-in components such as keep-alive
, component
, transition
, transition-group
etc., other user-defined component must be registered before use. Many students may encounter the following error messages during the development process:
'Unknown custom element: <xxx> - did you register the component correctly?
For recursive components, make sure to provide the "name" option.'
The general reason for this error is that we use unregistered components. Vue.js provides two ways to register components, global registration and partial registration. Next, we analyze these two registration methods from the perspective of source code analysis.
Global registration
To register a global component, you can use it Vue.component(tagName, options)
. E.g:
Vue.component('my-component', {
// 选项
})
// src/main.js
// 全局注册 Vue.component('app', App)
import Vue from 'vue'
import App from './App.vue'
Vue.component('app', App)
new Vue({
el: '#app',
template: '<app></app>'
})
// src/App.vue 不变
// src\components\HelloWorld.vue 不变
So, Vue.component
the function is when the definition of it, which is defined as the process occurs at 最开始初始化 Vue 的全局函数
the time of the code in src/core/global-api/assets.js
this case has not been performed in the new Vue :( ())
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) {
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
}
}
})
}
Function first traversal ASSET_TYPES
, to give type
after loading onto Vue. ASSET_TYPES
Defined in src/shared/constants.js
the:
export const ASSET_TYPES = [
'component',
'directive',
'filter'
]
Vue is so in fact 初始化了 3 个全局函数
, and if it type
is component
, and definition
is an object, then through this.opitons._base.extend
, equivalent to Vue.extend
converting the object into a constructor inherits Vue, and finally through this.options[type + 's'][id] = definition
the mount it Vue.options.components
on.
Because each of us is created by the components are Vue.extend
inherited, we have analyzed before such a period of logic in the succession process:
Sub.options = mergeOptions(
Super.options,
extendOptions
)
That it will Vue.options
merge into Sub.options
, which is the component options
on the stage and in the instance of the component, performs merge options
logic, to Sub.options.components
merge into vm.$options.components
the.
Then in the creation vnode
process, performs _createElement
the method, let's look at the logic of this part, which is defined in src/core/vdom/create-element.js
the:
export function _createElement (
context: Component,
tag?: string | Class<Component> | Function | Object,
data?: VNodeData,
children?: any,
normalizationType?: number
): VNode | Array<VNode> {
// ...
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 (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)
}
// ...
}
There is a logical judgment isDef(Ctor = resolveAsset(context.$options, 'components', tag))
, first look at resolveAsset
the definition, src/core/utils/options.js
in which:
/**
* Resolve an asset.
* This function is used because child instances need access
* to assets defined in its ancestor chain.
*/
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
}
This logic is very simple, first pass const assets = options[type]
got assets
, and then try to get assets[id]
, there is a sequence to be used directly id
take, if not, put id
becomes a 驼峰
form of shouting, if it still does not exist 在驼峰的基础上把首字母再变成大写
in the form of shouting, if Report an error if you still can't get it. This shows that we use Vue.component(id, definition)
when global registration component, id
can be 连字符
, 驼峰
or 首字母大写
form.
So back to our calling resolveAsset(context.$options, 'components', tag)
, namely to take vm.$options.components[tag]
, so that we can in resolveAsset
time to get the constructor of this component, and as a createComponent
parameter of the hook.
Partial registration
Vue.js also support local registration, we can use within a component components
options do local registration components, such as:
import HelloWorld from './components/HelloWorld'
export default {
components: {
HelloWorld
}
}
Actually understand the process of global registration, local registration is very simple. There is a phase of consolidation in the instance of a component of the Vue option
's logic, we have analyzed before, so put the components
merger into vm.$options.components
the, so that we can resolveAsset
get this time 组件的构造函数
, and as a createComponent
hook parameter.
Note that the local and global registration Registration is different, 只有该类型的组件才可以访问局部注册的子组
parts, and global registration is extended to the Vue.options
next, so all components created in the process, will be from the global Vue.options.components
expansion of the current components vm.$options.components
, the components of which is the global registration can be arbitrary Reason for use.
to sum up
Through the analysis of this section, we 组件的注册过程
have knowledge and understanding of 全局注册
and 局部注册
differences. In fact, in our daily work, when we use the component library, the more general basic components are often registered globally, while the business components of the prepared special scenarios are registered locally. Knowing their principles has a very good guiding significance for whether we use global registration components or partial registration components in our work.
PS: The
next section uses less asynchronous components, skip it for now, and make up later, and go directly to the next 深入响应式原理
chapter.
Vue source code learning directory
Vue source code learning-componentization (1) createComponent
Vue source code learning-componentization (2) patch
Vue source code learning-componentization (3) merge configuration
Vue source code learning-componentization (4) life cycle
Click to go back to the complete list of Vue source code learning
谢谢你阅读到了最后~
期待你关注、收藏、评论、点赞~
让我们一起 变得更强