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属性,都是由{组件名:组件函数}构成的,这样在后续使用时,可以直接利用实例内部的组件构建函数创建组件实例。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。