# 组件
# 注册
import("../vue.study.esm.js").then((module) => {
const { Vue } = module;
const Tag = Vue.component('tag', {
props: ['label'],
render(h) {
return h(
'span',
{
attrs: {
style: 'background: #ecf5ff; color: #409eff; border: 1px solid #d9ecff; border-radius: 4px; padding: 5px;'
}
},
this.label)
}
});
const app = new Vue({
components: {
tag2: Tag
},
render(h) {
return h('div', [
h('tag', {props: {label: 'Test'}}),
h('tag2', {props: {label: 'Test2'}})]
);
},
});
app.$mount("#app");
});
通过断点可发现全局注册Vue.component会调用this.options._base.extend(definition)(例子中实际为Vue.extend)生成子类构造方法,并将其注册到options上this.options[type + 's'][id] = definition,即保存在构造函数的options.components中。而私有组件则mergeOptions时直接保存在对象vm.$options中。

// src/core/global-api/assets.ts
ASSET_TYPES.forEach((type) => {
// @ts-expect-error function is not exact same 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)) {
// @ts-expect-error
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
}
}
})
// src/core/global-api/extend.ts
/**
* Class inheritance
*/
Vue.extend = function (extendOptions: any): typeof Component {
extendOptions = extendOptions || {}
const Super = this
const SuperId = Super.cid
const cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {})
if (cachedCtors[SuperId]) {
return cachedCtors[SuperId]
}
const name = extendOptions.name || Super.options.name
if (process.env.NODE_ENV !== 'production' && name) {
validateComponentName(name)
}
const Sub = (function VueComponent(this: any, options: any) {
this._init(options)
} as unknown) as typeof Component
Sub.prototype = Object.create(Super.prototype)
Sub.prototype.constructor = Sub
Sub.cid = cid++
Sub.options = mergeOptions(Super.options, extendOptions)
Sub['super'] = Super
// ...省略
return Sub
}
# 创建实例
在组件构造方法中打好断点,观察组件构建流程。回溯调用过程可知渲染_render创建的vnode中包含了组件的构造函数和相关参数,在__patch__时调用组件构造方法创建组件实例(带_isComponent参数)。在createComponent创建vnode时使用installComponentHooks(data)在vnode.data上绑定了init钩子,这个钩子里有创建组件实例及mount实例的代码。
init(vnode: VNodeWithData, hydrating: boolean): boolean | void {
if (
vnode.componentInstance &&
!vnode.componentInstance._isDestroyed &&
vnode.data.keepAlive
) {
// kept-alive components, treat as a patch
const mountedNode: any = vnode // work around flow
componentVNodeHooks.prepatch(mountedNode, mountedNode)
} else {
const child = (vnode.componentInstance = createComponentInstanceForVnode(
vnode,
activeInstance
))
child.$mount(hydrating ? vnode.elm : undefined, hydrating)
}
}
