Angular.js实现动态加载组件详解
前言
有时候需要根据URL来渲染不同组件,我所指的是在同一个URL地址中根据参数的变化显示不同的组件;这是利用Angular动态加载组件完成的,同时也会设法让这部分动态组件也支持AOT。
动态加载组件
下面以一个Step组件为示例,完成一个3个步骤的示例展示,并且可以通过URLuser?step=step-one的变化显示第N个步骤的内容。
1、resolveComponentFactory
首先,还是需要先创建动态加载组件模块。
import{Component,Input,ViewContainerRef,ComponentFactoryResolver,OnDestroy,ComponentRef}from'@angular/core'; @Component({ selector:'step', template:`` }) exportclassStepimplementsOnDestroy{ privatecurrentComponent:ComponentRef; constructor(privatevcr:ViewContainerRef,privatecfr:ComponentFactoryResolver){} @Input()setdata(data:{component:any,inputs?:{[key:string]:any}}){ constcompFactory=this.cfr.resolveComponentFactory(data.component); constcomponent=this.vcr.createComponent(compFactory); if(data.inputs){ for(letkeyindata.inputs){ component.instance[key]=data.inputs[key]; } } this.destroy(); this.currentComponent=component; } destroy(){ if(this.currentComponent){ this.currentComponent.destroy(); this.currentComponent=null; } } ngOnDestroy():void{ this.destroy(); } }
抛开一销毁动作不谈的话,实际就两行代码:
letcompFactory=this.cfr.resolveComponentFactory(this.comp);
利用ComponentFactoryResolver查找提供组件的ComponentFactory,而后利用这个工厂来创建实际的组件。
this.compInstance=this.vcr.createComponent(compFactory);
这一切都非常简单。
而对于一些基本的参数,是直接对组件实例进行赋值。
for(letkeyindata.inputs){ component.instance[key]=data.inputs[key]; }
最后,还需要告诉AngularAOT编译器为用户动态组件提供工厂注册,否则ComponentFactoryResolver会找不到它们,最简单就是利用NgModule.entryComponents进行注册。
@NgModule({ entryComponents:[UserOneComponent,UserTwoComponent,UserThirdComponent] }) exportclassAppModule{}
但这样其实还是挺奇怪的,entryComponents本身可能还会存在其他组件。而动态加载组件本身是一个通用性非常强,因此,把它封装成名曰StepModule挺有必要的,这样的话,就可以创建一种看起来更舒服的方式。
@NgModule({ declarations:[Step], exports:[Step] }) exportclassStepModule{ staticwithComponents(components:any){ return{ ngModule:StepModule, providers:[ {provide:ANALYZE_FOR_ENTRY_COMPONENTS,useValue:components,multi:true} ] } } }
通过利用ANALYZE_FOR_ENTRY_COMPONENTS将多个组件以更友好的方式动态注册至entryComponents。
constCOMPONENTS=[]; @NgModule({ declarations:[...COMPONENTS], imports:[ StepModule.withComponents([...COMPONENTS]) ] }) exportclassAppModule{}
2、一个示例
有3个Step步骤的组件,分别为:
//user-one.component.ts import{Component,OnDestroy,Input,Injector,EventEmitter,Output}from'@angular/core'; @Component({ selector:'step-one', template:`StepOneComponent:paramsvalue:{{step}}
` }) exportclassUserOneComponentimplementsOnDestroy{ private_step:string; @Input() setstep(str:string){ console.log('@Inputstep:'+str); this._step=str; } getstep(){ returnthis._step; } ngOnInit(){ console.log('steponeinit'); } ngOnDestroy():void{ console.log('steponedestroy'); } }
user-two、user-third略同,这里组件还需要进行注册:
constSTEPCOMPONENTS=[UserOneComponent,UserTwoComponent,UserThirdComponent]; @NgModule({ declarations:[...STEPCOMPONENTS], imports:[ StepModule.withComponents([...STEPCOMPONENTS]) ] }) exportclassAppModule{}
这里没有entryComponents字眼,而是为StepModule模块帮助我们动态注册。这样至少看起来更内聚一点,而且并不会与其他entryComponents在一起,待东西越多越不舒服。
最后,还需要UserComponent组件来维护步骤容器,会根据URL参数的变化,利用StepComponent组件动态加载相应组件。
@Component({ selector:'user', template:`` }) exportclassUserComponent{ constructor(privateroute:ActivatedRoute){} stepComp:any; ngOnInit(){ this.route.queryParams.subscribe(params=>{ conststep=params['step']||'step-one'; //组件与参数对应表 constcompMaps={ 'step-one':{component:UserOneComponent,inputs:{step:step}}, 'step-two':{component:UserTwoComponent}, 'step-third':{component:UserThirdComponent}, }; this.stepComp=compMaps[step]; }); } }
非常简单的使用,而且又对AOT比较友好。
总结
文章里面一直都在提AOT,其实AOT是Angular为了提供速度与包大小而生的,按我们项目的经验来看至少在包的大小可以减少到40%以上。
当然,如果你是用angularcli开发,那么,当你进行ngbuild--prod的时候,默认就已经开启AOT编译模式。
好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家毛票票的支持。