Angular.js实现动态加载组件详解


Posted in Javascript onMay 28, 2017

前言

有时候需要根据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 编译模式。

好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家三水点靠木的支持。

Javascript 相关文章推荐
多浏览器兼容的获取元素和鼠标的位置的js代码
Dec 15 Javascript
javascript 树形导航菜单实例代码
Aug 13 Javascript
文本框文本自动补全效果示例分享
Jan 19 Javascript
一个Action如何调用两个不同的方法
May 22 Javascript
jquery库文件略庞大用纯js替换jquery的方法
Aug 12 Javascript
layer弹出层框架alert与msg详解
Mar 14 Javascript
vue编译打包本地查看index文件的方法
Feb 23 Javascript
React router动态加载组件之适配器模式的应用详解
Sep 12 Javascript
详解如何在vue项目中使用lodop打印插件
Sep 27 Javascript
vue中promise的使用及异步请求数据的方法
Nov 08 Javascript
vue子路由跳转实现tab选项卡
Jul 24 Javascript
jQuery与原生JavaScript选择HTML元素集合用法对比分析
Nov 26 jQuery
利用node.js如何搭建一个简易的即时响应服务器
May 28 #Javascript
利用Angular.js编写公共提示模块的方法教程
May 28 #Javascript
Angular2入门教程之模块和组件详解
May 28 #Javascript
关于Angular2 + node接口调试的解决方案
May 28 #Javascript
对象不支持indexOf属性或方法的解决方法(必看)
May 28 #Javascript
设置cookie指定时间失效(实例代码)
May 28 #Javascript
Mac系统下Webstorm快捷键整理大全
May 28 #Javascript
You might like
mysq GBKl乱码
2006/11/28 PHP
php将时间差转换为字符串提示
2011/09/07 PHP
PHP大批量插入数据库的3种方法和速度对比
2014/07/08 PHP
给PHP开发者的编程指南 第一部分降低复杂程度
2016/01/18 PHP
yii通过小物件生成view的方法
2016/10/08 PHP
20个非常有用的PHP类库 加速php开发
2010/01/15 Javascript
innerHTML 和 getElementsByName 在IE下面的bug 的解决
2010/04/09 Javascript
jQuery+canvas实现的球体平抛及颜色动态变换效果
2016/01/28 Javascript
对jQuary选择器的全面总结
2016/06/20 Javascript
MUI 上拉刷新/下拉加载功能实例代码
2017/04/13 Javascript
nodejs实现大文件(在线视频)的读取
2020/10/16 NodeJs
vue watch自动检测数据变化实时渲染的方法
2018/01/16 Javascript
vuex(vue状态管理)的特殊应用案例分享
2020/03/03 Javascript
vue+vuex+axios从后台获取数据存入vuex,组件之间共享数据操作
2020/07/31 Javascript
python中使用mysql数据库详细介绍
2015/03/27 Python
win10下Python3.6安装、配置以及pip安装包教程
2017/10/01 Python
运用TensorFlow进行简单实现线性回归、梯度下降示例
2018/03/05 Python
python操作redis方法总结
2018/06/06 Python
Python拼接字符串的7种方法总结
2018/11/01 Python
Pycharm和Idea支持的vim插件的方法
2020/02/21 Python
Win 10下Anaconda虚拟环境的教程
2020/05/18 Python
纯css3实现的竖形无限级导航
2014/12/10 HTML / CSS
英国最大的体育&时尚零售公司:JD Sports
2017/12/13 全球购物
送给他或她的礼物:FUN.com
2018/08/17 全球购物
Ibood荷兰:互联网每日最佳在线优惠
2019/02/28 全球购物
动物学专业毕业生求职信
2013/10/11 职场文书
分家协议书
2014/04/21 职场文书
2014学习优秀共产党员先进事迹思想汇报
2014/09/14 职场文书
商场圣诞节活动总结
2015/05/06 职场文书
2015年秋季运动会前导词
2015/07/20 职场文书
安全教育主题班会教案
2015/08/12 职场文书
2016年春季运动会广播稿
2015/08/19 职场文书
利用Matlab绘制各类特殊图形的实例代码
2021/07/16 Python
Windows11性能真的上涨35%? 桌面酷睿i9实测结果公开
2021/11/21 数码科技
详解在OpenCV中如何使用图像像素
2022/03/03 Python
MySQL中order by的执行过程
2022/06/05 MySQL