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写的数字拼图小游戏代码[学习参考]
Oct 29 Javascript
js刷新框架子页面的七种方法代码
Nov 20 Javascript
jQuery中阻止冒泡事件的方法介绍
Apr 12 Javascript
javascript实现当前页导航激活的方法
Feb 27 Javascript
使用JQuery实现智能表单验证功能
Mar 08 Javascript
jQuery调用WebMethod(PageMethod) NET2.0的方法
Apr 15 Javascript
Vue.js绑定HTML class数组语法错误的原因分析
Oct 19 Javascript
jQuery实现一个简单的轮播图
Feb 19 Javascript
利用Javascript裁剪图片并存储的简单实现
Mar 13 Javascript
详解bootstrap用dropdown-menu实现上下文菜单
Sep 22 Javascript
vue实现图片预览组件封装与使用
Jul 13 Javascript
vue路由的配置和页面切换详解
Sep 09 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
?生?D片??C字串
2006/12/06 PHP
献给php初学者(入门学习经验谈)
2010/10/12 PHP
destoon二次开发常用数据库操作
2014/06/21 PHP
Symfony模板的快捷变量用法实例
2016/03/17 PHP
php rsa 加密,解密,签名,验签详解
2016/12/06 PHP
PHP批量删除jQuery操作
2017/07/23 PHP
laravel 中某一字段自增、自减的例子
2019/10/11 PHP
一些Javascript的IE和Firefox(火狐)兼容性的问题总结及常用例子
2009/05/21 Javascript
JavaScript 面向对象的之私有成员和公开成员
2010/05/04 Javascript
js 图片随机不定向浮动的实现代码
2013/07/02 Javascript
JS获取当前日期和时间的简单实例
2013/11/19 Javascript
js使下拉列表框可编辑不止是选择
2013/12/12 Javascript
javascript判断是否按回车键并解决浏览器之间的差异
2014/05/13 Javascript
封装好的javascript前端分页插件pagination
2016/01/04 Javascript
浅谈JavaScript对象的创建方式
2016/06/13 Javascript
浅析javascript中的Event事件
2016/12/09 Javascript
footer定位页面底部(代码分享)
2017/03/07 Javascript
jquery实现全选、全不选以及单选功能
2017/03/23 jQuery
Angularjs上传图片实例详解
2017/08/06 Javascript
layui之table checkbox初始化时选中对应选项的方法
2019/09/02 Javascript
通过原生vue添加滚动加载更多功能
2019/11/21 Javascript
Vue封装全局过滤器Filters的步骤
2020/09/16 Javascript
使用Python的Bottle框架写一个简单的服务接口的示例
2015/08/25 Python
PyQt5每天必学之拖放事件
2020/08/27 Python
python实现beta分布概率密度函数的方法
2019/07/08 Python
python写入数据到csv或xlsx文件的3种方法
2019/08/23 Python
解决Python3下map函数的显示问题
2019/12/04 Python
Python如何将函数值赋给变量
2020/04/28 Python
css3 transform导致子元素固定定位变成绝对定位的方法
2020/03/06 HTML / CSS
加拿大高尔夫超市:Golf Town
2018/01/12 全球购物
个人自我鉴定总结
2014/03/25 职场文书
安全先进个人材料
2014/12/29 职场文书
心术观后感
2015/06/11 职场文书
信息技术课教学反思
2016/02/23 职场文书
人力资源部工作计划
2019/05/14 职场文书
python 多态 协议 鸭子类型详解
2021/11/27 Python