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 相关文章推荐
JavaScript XML实现两级级联下拉列表
Nov 10 Javascript
jQuery控制Div拖拽效果完整实例分析
Apr 15 Javascript
微信小程序 参数传递详解
Oct 24 Javascript
JavaScript中递归实现的方法及其区别
Sep 12 Javascript
vue+express 构建后台管理系统的示例代码
Jul 19 Javascript
简单实现vue中的依赖收集与响应的方法
Feb 18 Javascript
Vue触发隐藏input file的方法实例详解
Aug 14 Javascript
解决node终端下运行js文件不支持ES6语法
Apr 04 Javascript
react PropTypes校验传递的值操作示例
Apr 28 Javascript
手动实现vue2.0的双向数据绑定原理详解
Feb 06 Vue.js
TypeScript中条件类型精读与实践记录
Oct 05 Javascript
输入框跟随文字内容适配宽实现示例
Aug 14 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
让PHP COOKIE立即生效,不用刷新就可以使用
2011/03/09 PHP
Zend的AutoLoad机制介绍
2012/09/27 PHP
ThinkPHP实现事务回滚示例代码
2014/06/23 PHP
基于jquery实现的服务器验证控件的启用和禁用代码
2010/04/27 Javascript
如何使用Jquery获取Form表单中被选中的radio值
2013/08/09 Javascript
简单js代码实现selece二级联动(推荐)
2014/02/18 Javascript
再谈Jquery Ajax方法传递到action(补充)
2014/05/12 Javascript
Nodejs全栈框架StrongLoop推荐
2014/11/09 NodeJs
使用js实现数据格式化
2014/12/03 Javascript
javascript实现当前页导航激活的方法
2015/02/27 Javascript
深入理解JavaScript系列(40):设计模式之组合模式详解
2015/03/04 Javascript
js canvas实现擦除动画
2016/07/16 Javascript
bootstrap中的 form表单属性role=&quot;form&quot;的作用详解
2017/01/20 Javascript
DOM事件探秘篇
2017/02/15 Javascript
AngularJS动态菜单操作指令
2017/04/25 Javascript
使用Angular CLI进行单元测试和E2E测试的方法
2018/03/24 Javascript
在vue中使用公共过滤器filter的方法
2018/06/26 Javascript
小程序转发探索示例
2019/02/19 Javascript
vue实现滑动超出指定距离回顶部功能
2019/07/31 Javascript
[02:42]岂曰无衣,与子同袍!DOTA2致敬每一位守护人
2020/02/17 DOTA
跨平台python异步回调机制实现和使用方法
2013/11/26 Python
python3.6使用pickle序列化class的方法
2018/10/22 Python
pycharm重命名文件的方法步骤
2019/07/29 Python
Django使用 Bootstrap 样式修改书籍列表过程解析
2019/08/09 Python
Python用requests库爬取返回为空的解决办法
2021/02/21 Python
css3实现波纹特效、H5实现动态波浪效果
2018/01/31 HTML / CSS
大学生应聘自荐信
2013/10/11 职场文书
工程开工庆典邀请函
2014/02/01 职场文书
银行服务感言
2014/03/01 职场文书
个人租房协议书
2014/04/09 职场文书
环保口号大全
2014/06/12 职场文书
2015年老干部工作总结
2015/04/23 职场文书
2015年“我们的节日·中秋节”活动总结
2015/07/30 职场文书
python基于tkinter实现gif录屏功能
2021/05/19 Python
php修改word的实例方法
2021/11/17 PHP
vue如何在data中引入图片的正确路径
2022/06/05 Vue.js