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 相关文章推荐
尽可能写&quot;友好&quot;的&quot;Javascript&quot;代码
Jan 09 Javascript
JQuery打造PHP的AJAX表单提交实例
Nov 03 Javascript
GreyBox技术总结(转)
Nov 23 Javascript
jQuery之自动完成组件的深入解析
Jun 19 Javascript
js控制再次点击按钮之间的间隔时间可防止重复提交
Aug 01 Javascript
jQuery实现简洁的导航菜单效果
Nov 23 Javascript
javaScript如何跳出多重循环break、continue
Sep 01 Javascript
Seajs是什么及sea.js 由来,特点以及优势
Oct 13 Javascript
smartupload实现文件上传时获取表单数据(推荐)
Dec 12 Javascript
微信小程序仿微信运动步数排行(交互)
Jul 13 Javascript
vue自定v-model实现表单数据双向绑定问题
Sep 03 Javascript
详解JS深拷贝与浅拷贝
Aug 04 Javascript
利用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
利用discuz自带通行证整合dedecms的方法以及文件下载
2007/03/06 PHP
PHP生成唯一订单号
2015/07/05 PHP
Laravel手动分页实现方法详解
2016/10/09 PHP
使用JavaScript switch case 另类写法
2010/03/14 Javascript
JavaScript Array Flatten 与递归使用介绍
2011/10/30 Javascript
jquery实现顶部向右伸缩的导航区域代码
2015/09/02 Javascript
JavaScript代码因逗号不规范导致IE不兼容的问题
2016/02/25 Javascript
浅谈JavaScript前端开发的MVC结构与MVVM结构
2016/06/03 Javascript
AngularJS基础 ng-value 指令简单示例
2016/08/03 Javascript
AngularJS实现标签页的两种方式
2016/09/05 Javascript
JavaScript 闭包详细介绍
2016/09/28 Javascript
js实现PC端根据IP定位当前城市地理位置
2017/02/22 Javascript
vue2.0 自定义日期时间过滤器
2017/06/07 Javascript
JavaScript使用FileReader实现图片上传预览效果
2020/03/27 Javascript
Nodejs实现文件上传的示例代码
2017/09/26 NodeJs
JS删除数组里的某个元素方法
2018/02/03 Javascript
利用JavaScript的Map提升性能的方法详解
2019/08/14 Javascript
JS实现移动端在线签协议功能
2019/08/22 Javascript
基于javascript的无缝滚动动画实现2
2020/08/07 Javascript
python获取指定路径下所有指定后缀文件的方法
2015/05/26 Python
Python简单连接MongoDB数据库的方法
2016/03/15 Python
python实现决策树C4.5算法详解(在ID3基础上改进)
2017/05/31 Python
Python数据分析之双色球中蓝红球分析统计示例
2018/02/03 Python
Django框架多表查询实例分析
2018/07/04 Python
使用 Python 实现简单的 switch/case 语句的方法
2018/09/17 Python
python输入整条数据分割存入数组的方法
2018/11/13 Python
使用pandas实现csv/excel sheet互相转换的方法
2018/12/10 Python
PyQt5实现简单数据标注工具
2019/03/18 Python
TensorFlow2.0矩阵与向量的加减乘实例
2020/02/07 Python
教师评优事迹材料
2014/01/10 职场文书
给老师的一封建议书
2014/03/13 职场文书
2014购房个人委托书范本
2014/10/12 职场文书
安全教育第一课观后感
2015/06/17 职场文书
创业计划书之餐饮
2019/09/02 职场文书
python pygame入门教程
2021/06/01 Python
vue项目配置sass及引入外部scss文件
2022/04/14 Vue.js