vue版本号2.6.12,为方便分析,选择了runtime+compiler版本。
如果有感兴趣的同学可以看看我之前的源码分析文章,这里呈上链接:《Vue源码分析系列:目录》
Vue的组件祖册分为两部分,一部分为全局组件注册,也就是使用Vue.component(tag,options)
,另外一部分为局部组件注册,使用options.component = {App}
。
我们先来看全局组件注册。
import HelloWorld from './components/HelloWorld';
Vue.component('HelloWorld', HelloWorld);
new Vue({
render: h => h(App)
}).$mount('#app');
这里使用了Vue.component
注册了一个HelloWorld
全局组件。
首先看Vue.component
。
Vue.component
Vue.component
定义在global-api/assets.js
中:
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
}
}
})
}
首先遍历ASSET_TYPES
:
export const ASSET_TYPES = [
'component',
'directive',
'filter'
]
继续向下看initAssetRegisters
:
if (type === 'component' && isPlainObject(definition)) {
definition.name = definition.name || id
definition = this.options._base.extend(definition)
}
当type
为component
且definition
为一个对象的时候,执行Vue.extend
,传入component
的options
。
之前我们介绍过Vue.extend
,这个方法里面会执行一个mergeOptions
:
Sub.options = mergeOptions(Super.options, extendOptions);
将Vue内置的options
与刚刚传入的组件的options
合并,并赋值在组件构造器的options
属性内。
所以最终definition
拿到的是一个合并options
后的组件的构造器。
继续往下看:
this.options[type + 's'][id] = definition;
return definition
将definition
放到Vue的components.id
上,然后返回definition
。
也就是说,Vue.component("HelloWorld",HelloWorld);
之后,Vue的components.HelloWorld
就是一个HelloWorld
组件的构造器了。
之后运行new Vue
,进入_init
,mergeOptions
进行配置合并。这里的components
的合并策略和生命周期函数的合并策略并不一样:
function mergeAssets(
parentVal: ?Object,
childVal: ?Object,
vm?: Component,
key: string
): Object {
const res = Object.create(parentVal || null);
if (childVal) {
process.env.NODE_ENV !== "production" &&
assertObjectType(key, childVal, vm);
return extend(res, childVal);
} else {
return res;
}
}
这里是将Vue.components
合并在了options.components
的__proto__
上。
再往下运行就是进入_render
,然后再进入_createElement
,其中_createElement
中有一个判断:
else if (
(!data || !data.pre) &&
isDef((Ctor = resolveAsset(context.$options, "components", tag)))
) {
//创建一个组件vnode
// component
vnode = createComponent(Ctor, data, context, children, tag);
}
会去寻找vm.$options.components.tag
对应的组件构造器,如果找到了就将构造器传入createComponent
,生成一个组件vnode用于之后的_patch_
。
到这里,全局组件的注册过程就解析完毕了。
其实了解了全局组件的注册过程后,局部组件的注册过程理解起来就更加简单了。其实局部组件注册只是执行了刚刚提到的_init
中的mergeOptions
,将子组件的options
merge到父组件的vm.$options
上。