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 相关文章推荐
phpwind放自动注册方法
Dec 02 Javascript
js禁止小键盘输入数字功能代码
Aug 01 Javascript
js判断生效时间不得大于失效时间的思路及代码
Apr 23 Javascript
jquery实现div拖拽宽度示例代码
Jul 31 Javascript
密码框显示提示文字jquery示例
Aug 29 Javascript
jQuery操作select下拉框的text值和value值的方法
May 31 Javascript
jQuery中ajax和post处理json的不同示例对比
Nov 02 Javascript
JavaScript中的类(Class)详细介绍
Dec 30 Javascript
弹出遮罩层后禁止滚动效果【实现代码】
Apr 29 Javascript
jQuery查找dom的几种方法效率详解
May 17 jQuery
微信小程序提取公用函数到util.js及使用方法示例
Jan 10 Javascript
Vue + iView实现Excel上传功能的完整代码
Jun 22 Vue.js
利用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
PHP+FLASH实现上传文件进度条相关文件 下载
2007/07/21 PHP
php session处理的定制
2009/03/16 PHP
Ajax+PHP边学边练 之五 图片处理
2009/12/03 PHP
把1316这个数表示成两个数的和,其中一个为13的倍数,另一个是11的倍数,求这两个数。
2011/06/24 PHP
作为PHP程序员应该了解MongoDB的五件事
2013/06/03 PHP
PHP设计模式之迭代器模式的深入解析
2013/06/13 PHP
PHP将Excel导入数据库及数据库数据导出至Excel的方法
2015/06/24 PHP
php获取flash尺寸详细数据的方法
2016/11/12 PHP
什么是MEAN?JavaScript编程中的MEAN是什么意思?
2014/12/18 Javascript
js正则匹配出所有图片及图片地址src的方法
2015/06/08 Javascript
理解javascript中的MVC模式
2016/01/28 Javascript
AngularJS实现页面定时刷新
2017/03/14 Javascript
基于jquery的on和click的区别详解
2018/01/15 jQuery
jQuery+ajax读取json数据并按照价格排序示例
2018/03/28 jQuery
讲解vue-router之什么是嵌套路由
2018/05/28 Javascript
vue中v-for通过动态绑定class实现触发效果
2018/12/06 Javascript
vue使用axios上传文件(FormData)的方法
2019/04/14 Javascript
JavaScript 截取字符串代码实例
2019/09/05 Javascript
vue实现简单计算商品价格
2020/09/14 Javascript
微信小程序实现自定义动画弹框/提示框的方法实例
2020/11/06 Javascript
python转换摩斯密码示例
2014/02/16 Python
Python异常学习笔记
2015/02/03 Python
详解Python中的type()方法的使用
2015/05/21 Python
Python建立Map写Excel表实例解析
2018/01/17 Python
django settings.py 配置文件及介绍
2019/07/15 Python
python matplotlib库绘制条形图练习题
2019/08/10 Python
Django web自定义通用权限控制实现方法
2020/11/24 Python
使用html5 canvas绘制圆环动效
2019/06/03 HTML / CSS
芬兰攀岩、山地运动和户外活动用品购物网站:Bergfreunde
2016/10/06 全球购物
国际贸易专业个人求职信范文分享
2013/12/14 职场文书
有限公司股东合作协议书
2014/10/29 职场文书
政工师工作总结2015
2015/05/26 职场文书
社区服务理念口号
2015/12/25 职场文书
Java存储没有重复元素的数组
2022/04/29 Java/Android
Java实现简单小画板
2022/06/10 Java/Android
二维码条形码生成的JavaScript脚本库
2022/07/07 Javascript