Vue如何实现组件的源码解析
官网上关于组件继承分为两大类,全局组件和局部组件。无论哪种方式,最核心的是创建组件,然后根据场景不同注册组件。
有一点要牢记,“Vue.js组件其实都是被扩展的Vue实例”!
1.全局组件
//方式一
varMyComponent=Vue.extend({
name:'my-component',
template:'Acustomcomponent!'
});
Vue.component('my-component',MyComponent);
//方式二
Vue.component('my-component',{
name:'my-component',
template:'Acustomcomponent!'
});
//使用组件
主要涉及到两个静态方法:
- Vue.extend:通过扩展Vue实例的方法创建组件
- Vue.component:注册组件
先来看看Vue.extend源码,解释参考中文注释:
Vue.extend=function(extendOptions){
extendOptions=extendOptions||{};
varSuper=this;
varisFirstExtend=Super.cid===0;
if(isFirstExtend&&extendOptions._Ctor){
returnextendOptions._Ctor;
}
varname=extendOptions.name||Super.options.name;
//如果有name属性,即组件名称,检测name拼写是否合法
if('development'!=='production'){
if(!/^[a-zA-Z][\w-]*$/.test(name)){
warn('Invalidcomponentname:"'+name+'".Componentnames'+'canonlycontainalphanumericcharacatersandthehyphen.');
name=null;
}
}
//创建一个VueComponent构造函数,函数名为‘VueComponent'或者name
varSub=createClass(name||'VueComponent');
//构造函数原型继承Vue.prototype
Sub.prototype=Object.create(Super.prototype);
Sub.prototype.constructor=Sub;
Sub.cid=cid++;
//合并Vue.options和extendOptions,作为新构造函数的静态属性options
Sub.options=mergeOptions(Super.options,extendOptions);
//'super'静态属性指向Vue函数
Sub['super']=Super;
//start-----------------拷贝Vue静态方法
//allowfurtherextension
Sub.extend=Super.extend;
//createassetregisters,soextendedclasses
//canhavetheirprivateassetstoo.
config._assetTypes.forEach(function(type){
Sub[type]=Super[type];
});
//end-----------------拷贝Vue静态方法
//enablerecursiveself-lookup
if(name){
Sub.options.components[name]=Sub;
}
//cacheconstructor:缓存该构造函数
if(isFirstExtend){
extendOptions._Ctor=Sub;
}
returnSub;
};
可以看到,Vue.extend的关键点在于:创建一个构造函数functionVueComponent(options){this._init(options)},通过原型链继承Vue原型上的属性和方法,再讲Vue的静态函数赋值给该构造函数。
再看看Vue.component源码,解释参考中文注释:
//_assetTypes:['component','directive','elementDirective','filter','transition','partial']
config._assetTypes.forEach(function(type){
//静态方法Vue.component
Vue[type]=function(id,definition){
if(!definition){
returnthis.options[type+'s'][id];
}else{
/*istanbulignoreif*/
if('development'!=='production'){
if(type==='component'&&(commonTagRE.test(id)||reservedTagRE.test(id))){
warn('Donotusebuilt-inorreservedHTMLelementsascomponent'+'id:'+id);
}
}
//如果第二个参数是简单对象,则需要通过Vue.extend创建组件构造函数
if(type==='component'&&isPlainObject(definition)){
if(!definition.name){
definition.name=id;
}
definition=Vue.extend(definition);
}
//将组件函数加入Vue静态属性options.components中,也就是,全局注入该组件
this.options[type+'s'][id]=definition;
returndefinition;
}
};
});
方法Vue.component的关键点是,将组件函数注入到Vue静态属性中,这样可以根据组件名称找到对应的构造函数,从而创建组件实例。
2.局部组件
varMyComponent=Vue.extend({
template:'Acustomcomponent!'
});
newVue({
el:'#example',
components:{
'my-component':MyComponent,
'other-component':{
template:'Acustomcomponent!'
}
}
});
注册局部组件的特点就是在创建Vue实例的时候,定义components属性,该属性是一个简单对象,key值为组件名称,value可以是具体的组件函数,或者创建组件必须的options对象。
来看看Vue如何解析components属性,解释参考中文注释:
Vue.prototype._init=function(options){
options=options||{};
....
//mergeoptions.
options=this.$options=mergeOptions(this.constructor.options,options,this);
...
};
functionmergeOptions(parent,child,vm){
//解析components属性
guardComponents(child);
guardProps(child);
...
}
functionguardComponents(options){
if(options.components){
//将对象转为数组
varcomponents=options.components=guardArrayAssets(options.components);
//ids数组包含组件名
varids=Object.keys(components);
vardef;
if('development'!=='production'){
varmap=options._componentNameMap={};
}
//遍历组件数组
for(vari=0,l=ids.length;ikebab-casemappingfor
//possiblecustomelementcaseerrorwarning
if('development'!=='production'){
map[key.replace(/-/g,'').toLowerCase()]=hyphenate(key);
}
def=components[key];
//如果是组件定义是简单对象-对象字面量,那么需要根据该对象创建组件函数
if(isPlainObject(def)){
components[key]=Vue.extend(def);
}
}
}
}
在创建Vue实例过程中,经过guardComponents()函数处理之后,能够保证该Vue实例中的components属性,都是由{组件名:组件函数}构成的,这样在后续使用时,可以直接利用实例内部的组件构建函数创建组件实例。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。