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编译模式。
好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家毛票票的支持。