Angular动态加载组件

栏目: 编程语言 · AngularJS · 发布时间: 8年前

内容简介:Angular动态加载组件

引言

有时候需要根据URL来渲染不同组件,我所指的是在同一个URL地址中根据参数的变化显示不同的组件;这是利用Angular动态加载组件完成的,同时也会设法让这部分动态组件也支持AOT。

动态加载组件

下面以一个Step组件为示例,完成一个3个步骤的示例展示,并且可以通过URL user?step=step-one 的变化显示第N个步骤的内容。

1、resolveComponentFactory

首先,还是需要先创建动态加载组件模块。

import { Component, Input, ViewContainerRef, ComponentFactoryResolver, OnDestroy, ComponentRef } from '@angular/core';
@Component({
  selector: 'step',
  template: ``
})
export class Step implements OnDestroy {
  private currentComponent: ComponentRef<any>;

  constructor(private vcr: ViewContainerRef, private cfr: ComponentFactoryResolver) {}

  @Input() set data(data: { component: any, inputs?: { [key: string]: any } } ) {
      const compFactory = this.cfr.resolveComponentFactory(data.component);
      const component = this.vcr.createComponent(compFactory);
      if (data.inputs) {
        for (let key in data.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();
  }

}

抛开一销毁动作不谈的话,实际就两行代码:

let compFactory = this.cfr.resolveComponentFactory(this.comp);

利用 ComponentFactoryResolver 查找提供组件的 ComponentFactory ,而后利用这个工厂来创建实际的组件。

this.compInstance = this.vcr.createComponent(compFactory);

这一切都非常简单。

而对于一些基本的参数,是直接对组件实例进行赋值。

for (let key in data.inputs) {
          component.instance[key] = data.inputs[key];
        }

最后,还需要告诉Angular AOT编译器为用户动态组件提供工厂注册,否则 ComponentFactoryResolver 会找不到它们,最简单就是利用 NgModule.entryComponents 进行注册。

@NgModule({
  entryComponents: [ UserOneComponent, UserTwoComponent, UserThirdComponent ]
})
export class AppModule { }

但这样其实还是挺奇怪的, entryComponents 本身可能还会存在其他组件。而动态加载组件本身是一个通用性非常强,因此,把它封装成名曰 StepModule 挺有必要的,这样的话,就可以创建一种看起来更舒服的方式。

@NgModule({
  declarations: [ Step ],
  exports: [ Step ]
})
export class StepModule {
  static withComponents(components: any) {
    return {
      ngModule: StepModule,
      providers: [
        { provide: ANALYZE_FOR_ENTRY_COMPONENTS, useValue: components, multi: true }
      ]
    }
  }
}

通过利用 ANALYZE_FOR_ENTRY_COMPONENTS 将多个组件以更友好的方式动态注册至 entryComponents

const COMPONENTS = [  ];

@NgModule({
  declarations: [ ...COMPONENTS ],
  imports: [
    StepModule.withComponents([ ...COMPONENTS ])
  ]
})
export class AppModule { }

2、一个示例

有3个Step步骤的组件,分别为:

// user-one.component.ts
import { Component, OnDestroy, Input, Injector, EventEmitter, Output } from '@angular/core';
@Component({
  selector: 'step-one',
  template: `<h2>Step One Component:params value: {{step}}</h2>`
})
export class UserOneComponent implements OnDestroy {
  private _step: string;
  @Input() 
  set step(str: string) {
    console.log('@Input step: ' + str);
    this._step = str;
  }
  get step() {
    return this._step;
  }
  
  ngOnInit() {
    console.log('step one init');
  }
  ngOnDestroy(): void {
    console.log('step one destroy');
  }

}

user-two、user-third 略同,这里组件还需要进行注册:

const STEPCOMPONENTS = [ UserOneComponent, UserTwoComponent, UserThirdComponent ];

@NgModule({
  declarations: [ ...STEPCOMPONENTS ],
  imports: [
    StepModule.withComponents([ ...STEPCOMPONENTS ])
  ]
})
export class AppModule { }

这里没有 entryComponents 字眼,而是为 StepModule 模块帮助我们动态注册。这样至少看起来更内聚一点,而且并不会与其他 entryComponents 在一起,待东西越多越不舒服。

最后,还需要 UserComponent 组件来维护步骤容器,会根据 URL 参数的变化,利用 StepComponent 组件动态加载相应组件。

@Component({
  selector: 'user',
  template: `<step [comp]="stepComp"></step>`
})
export class UserComponent {
  constructor(private route: ActivatedRoute) {}
  stepComp: any;
  ngOnInit() {
    this.route.queryParams.subscribe(params => {
      const step = params['step'] || 'step-one';
      // 组件与参数对应表
      const compMaps = {
        'step-one': { component: UserOneComponent, inputs: { step: step } },
        'step-two': { component: UserTwoComponent },
        'step-third': { component: UserThirdComponent },
      };
      this.stepComp = compMaps[step];
    });
  }
}

非常简单的使用,而且又对AOT比较友好。

总结

文章里面一直都在提AOT,其实AOT是Angular为了提供速度与包大小而生的,按我们项目的经验来看至少在包的大小可以减少到 40% 以上。

当然,如果你是用angular cli开发,那么,当你进行 ng build --prod 的时候,默认就已经开启 AOT 编译模式。


以上所述就是小编给大家介绍的《Angular动态加载组件》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

云攻略

云攻略

马克·贝尼奥夫、卡莱尔·阿德勒 / 徐杰 / 海天出版社 / 2010年8月 / 36.00元

Apple、Google、甲骨文、腾讯 都已投入了云的怀抱, 你还在等什么? 快来加入我们! 最初,Salesforce.com 只是一间小小的租赁公寓 在短短10年内 它已成长为 世界上发展最快、最具创新力的 产业变革领导者 曾经,这是个软件为王的时代。 现在,这是个云计算的新时代。 NO SOFTWARE 抛弃软件的......一起来看看 《云攻略》 这本书的介绍吧!

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具