环境: Angular 13.x.x

angular中支持可以通过路由来懒加载某些页面模块已达到减少首屏尺寸, 提高首屏加载速度的目的. 但是这种通过路由的方式有时候是无法满足需求的.

比如, 点击一个按钮后显示一行工具栏, 这个工具栏组件我不希望它默认打包进main.js, 而是用户点按钮后动态把组件加载并显示出来.

那为什么要动态加载呢? 如果直接在目标页面组件引入工具栏组件, 那么工具栏组件中的代码就会被打包进目标页面组件所在的模块, 这会导致目标页面组件所在的模块生成的js体积变大; 通过动态懒加载的方式, 可以让工具栏组件只在用户点了按钮后再加载, 这样就可以达到减少首屏尺寸的目的.

新建一个angular项目

为了演示, 新建一个angular项目, 然后再新建一个ToolbarModule, 项目的目录结构如图

为了达到演示的目的, 我在ToolbarModule的html模板中放了个将近1m的base64图片, 然后直接在AppModule中引用ToolbarModule, 然后执行ng build, 执行结果如图

可以看到打包尺寸到达了1.42mb, 也就是说用户每次刷新这个页面, 不管用户有没有点击显示工具栏按钮, 工具栏组件相关的内容都会被加载出来, 这造成了资源的浪费, 所以下面将ToolbarModuleAppModuleimports声明中移除, 然后在用户点击首次点击显示时懒加载工具栏组件.

懒加载工具栏组件

首先, 新建一个ToolbarModuleToolbarComponent, 并在ToolbarModule声明ToolbarComponent

toolbar.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ToolbarComponent } from './toolbar.component';
@NgModule({
    declarations: [ToolbarComponent],
    imports: [CommonModule],
    exports: [ToolbarComponent],
})
class ToolbarModule {}
export { ToolbarComponent, ToolbarModule };

toolbar.component.ts

import { Component, OnInit } from '@angular/core';
@Component({
    selector: 'toolbar',
    templateUrl: './toolbar.component.html',
    styles: [
        `
    svg {
      width: 64px;
      height: 64px;
    }
    img {
      width: 64px;
      height: 64px;
      object-fit: cover;
    }
    `,
    ],
})
export class ToolbarComponent implements OnInit {
    constructor() {}
    ngOnInit(): void {}
}

toolbar.component.html

<div class="flex">
  <svg t="1652618923451" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2104" width="200" height="200"><path d="M412 618m-348 0a348 348 0 1 0 696 0 348 348 0 1 0-696 0Z" fill="#C9F4EB" p-id="2105"></path><path d="M673.19 393h-333a25 25 0 0 1 0-50h333a25 25 0 0 1 0 50zM600.89 235H423.11C367.91 235 323 190.28 323 135.32v-12.5a25 25 0 0 1 50 0v12.5c0 27.39 22.48 49.68 50.11 49.68h177.78c27.63 0 50.11-22.29 50.11-49.68v-16.5a25 25 0 1 1 50 0v16.5c0 54.96-44.91 99.68-100.11 99.68zM673.19 585.5h-333a25 25 0 0 1 0-50h333a25 25 0 0 1 0 50zM467 778H340a25 25 0 0 1 0-50h127a25 25 0 0 1 0 50z" fill="#087E6A" p-id="2106"></path><path d="M739.76 952H273.62a125.14 125.14 0 0 1-125-125V197a125.14 125.14 0 0 1 125-125h466.14a125.14 125.14 0 0 1 125 125v630a125.14 125.14 0 0 1-125 125zM273.62 122a75.08 75.08 0 0 0-75 75v630a75.08 75.08 0 0 0 75 75h466.14a75.08 75.08 0 0 0 75-75V197a75.08 75.08 0 0 0-75-75z" fill="#087E6A" p-id="2107"></path></svg>
  <svg t="1652618941842"
       class="icon"
       viewBox="0 0 1024 1024"
       version="1.1"
       xmlns="http://www.w3.org/2000/svg"
       p-id="2247"
       width="200"
       height="200">
    <path d="M415 624m-348 0a348 348 0 1 0 696 0 348 348 0 1 0-696 0Z"
          fill="#C9F4EB"
          p-id="2248"></path>
    <path d="M695 790H362a25 25 0 0 1 0-50h333a25 25 0 0 1 0 50zM583 649H362a25 25 0 0 1 0-50h221a25 25 0 0 1 0 50zM262 287H129a25 25 0 0 1 0-50h133a25 25 0 0 1 0 50zM262 455.33H129a25 25 0 1 1 0-50h133a25 25 0 0 1 0 50zM262 623.67H129a25 25 0 0 1 0-50h133a25 25 0 0 1 0 50zM262 792H129a25 25 0 0 1 0-50h133a25 25 0 0 1 0 50z"
          fill="#087E6A"
          p-id="2249"></path>
    <path d="M761.76 964H295.62a125.14 125.14 0 0 1-125-125V209a125.14 125.14 0 0 1 125-125h466.14a125.14 125.14 0 0 1 125 125v630a125.14 125.14 0 0 1-125 125zM295.62 134a75.09 75.09 0 0 0-75 75v630a75.08 75.08 0 0 0 75 75h466.14a75.08 75.08 0 0 0 75-75V209a75.09 75.09 0 0 0-75-75z"
          fill="#087E6A"
          p-id="2250"></path>
    <path d="M617 376H443a25 25 0 0 1 0-50h174a25 25 0 0 1 0 50z"
          fill="#087E6A"
          p-id="2251"></path>
    <path d="M530 463a25 25 0 0 1-25-25V264a25 25 0 0 1 50 0v174a25 25 0 0 1-25 25z"
          fill="#087E6A"
          p-id="2252"></path>
  </svg>
  <img src="<这里应该是一张大小将近1M的base64图片, 内容较大, 就略去了...>" alt="">
</div>

然后再AppComponent的中按钮点击事件处理程序中写加载工具栏模块的代码:

app.component.ts

import { Component, createNgModuleRef, Injector, ViewChild, ViewContainerRef } from '@angular/core';
@Component({
    selector: 'root',
    template: `
               <div class="container h-screen flex items-center flex-col w-100 justify-center">
                 <div class="mb-3"
                      [ngClass]="{ hidden: !isToolbarVisible }">
                   <ng-container #toolbar></ng-container>
                 </div>
                 <div>
                   <button (click)="toggleToolbarVisibility()"
                           class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">{{ isToolbarVisible ? '隐藏' : '显示' }}</button>
                   <p class="mt-3">首屏内容</p>
                 </div>
               </div>
             `,
})
export class AppComponent {
    title = 'ngx-lazy-load-demo';
    toolbarLoaded = false;
    isToolbarVisible = false;
    @ViewChild('toolbar', { read: ViewContainerRef }) toolbarViewRef!: ViewContainerRef;
    constructor(private _injector: Injector) {}
    toggleToolbarVisibility() {
        this.isToolbarVisible = !this.isToolbarVisible;
        this.loadToolbarModule().then();
    }
    private async loadToolbarModule() {
        if (this.toolbarLoaded) return;
        this.toolbarLoaded = true;
        const { ToolbarModule, ToolbarComponent } = await import('./toolbar/toolbar.module');
        const moduleRef = createNgModuleRef(ToolbarModule, this._injector);
        const { injector } = moduleRef;
        const componentRef = this.toolbarViewRef.createComponent(ToolbarComponent, {
            injector,
            ngModuleRef: moduleRef,
        });
    }
}

关键在于其中的第32-42行, 首先通过一个动态import导入toolbar.module.ts中的模块, 然后调用createNgModuleRef并传入当前组件的Injector作为ToolbarModule的父级Injector, 这样就实例化了ToolbarModule得到了moduleRef对象, 最后就是调用html模板中声明的<ng-container #toolbar></ng-container>的ViewContainerRef对象的createComponent方法创建ToolbarComponent组件

private async loadToolbarModule() {
    if (this.toolbarLoaded) return;
    this.toolbarLoaded = true;
    const { ToolbarModule, ToolbarComponent } = await import('./toolbar/toolbar.module');
    const moduleRef = createNgModuleRef(ToolbarModule, this._injector);
    const { injector } = moduleRef;
    const componentRef = this.toolbarViewRef.createComponent(ToolbarComponent, {
        injector,
        ngModuleRef: moduleRef,
    });
}

此时再来看下这番操作后执行ng build打包的尺寸大小

可以看到首屏尺寸没有开头那么离谱了, 原因是没有在AppModuleAppComponent直接导入ToolbarModuleToolbarComponent,ToolbarModule被打进了另外的js文件中(Lazy Chunk Files), 当首次点击显示按钮时, 就会加载这个包含ToolbarModule的js文件

注意看下面的gif演示中, 首次点击显示按钮, 浏览器网络调试工具中会多出一个对src_app_toolbar_toolbar_module_ts.js文件的请求

以上就是Angular懒加载动态创建显示该模块下声明的组件的详细内容,更多关于Angular懒加载动态声明组件的资料请关注Devmax其它相关文章!

Angular懒加载动态创建显示该模块下声明的组件的更多相关文章

  1. Html5实现首页动态视频背景的示例代码

    这篇文章主要介绍了Html5实现首页动态视频背景的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  2. ios – 在Swift中动态创建uiviewcontroller

    我想动态创建UIViewController而不创建类或使用Mainstoryboard.我希望它以编程方式发生.这可能吗?

  3. 使用iOS故事板动态调整UILabel的高度

    我有一个标签,它是使用iOSStoryboard布局创建的.但是,标签的内容是动态的,可以在运行时更改.如何确保根据标签中的内容调整标签的高度.我试过了:将行数设置为0设置编辑器–>适合内容的大小.但它们不起作用.标签中的文本仅以单行打印,因此某些文本不会出现在屏幕上.任何帮助将受到高度赞赏.解决方法试试这种方式你的标签应该是0的行数给标签赋予高度约束并选择高度约束然后设置大于等于,它将根据内容自动调整高度

  4. ios – 如何以编程方式动态地对UIButton的背景图像进行着色?

    我正在开发一个应用程序–或者更确切地说是一些可重用的“框架”,我很乐意在它工作时分享它.在此应用程序中,用户应该能够从颜色主题列表中进行选择.因此,应用程序必须能够以某种相当动态的方式对其UI元素进行着色.对于按钮,所有着色都不起作用.必须在此处提供正确着色的背景图像.但是为每个人准备一套背景图像只是第二好的.它不够动态和灵活.最后,解决方案可能归结为为所选和正常状态提供一个单色(灰色)梯度图像,

  5. ios – Firebase动态链接中的customURLScheme是什么?

    在documentation中它说要将以下行添加到我的AppDelegate.swift:根据我的理解,这应该是您在info.plist中添加的相同链接.但是,我很困惑为什么在quickstart-iosrepo他们决定将其等同于“dlscheme”.任何人都可以帮我理解这个方案究竟是什么?

  6. ios – 在动态构建的分段控件的导航栏中自动调整大小

    控制器将UISegmentedControl添加到导航栏.分段控件添加到控制器的viewDidLoad方法的导航栏中,但实际的段是在调用viewDidLoad后动态创建的.显示视图时,我无法自动调整分段大小.他们都被挤压,likeinthispost,虽然这里的决议不适用.如果在将分段控件添加到导航栏的右侧项目之前添加了段,则会自动调整它们的大小并在显示视图时看起来很好.这是我的代码的精简版本,如下所示.我错过了什么?

  7. Autolayout iOS 6动态表格单元格高度

    我有UITableviewCell子类.在这个单元格中,我有2个标签和一个显示评级星的视图.我想要lbl评论的动态高度来适应所有的文本.它应该扩大&根据评论的长度收缩高度.我已经实现了这一点,但没有AutoLayout如下现在我使用AutoLayout功能.如何使用Autolayout实现这一点?

  8. 动态模拟iOS动态类型系统文字大小(UIContentSizeCategory)

    解决方法多么尴尬!

  9. ios – 链接动态(Cocoa Touch)框架内的静态库

    我有一个链接到谷歌地图的动态框架(据我所见,它仍然是一个静态库,如果不是这样,只是一个框架包装).问题是,框架与静态库链接,并且似乎直接包含其代码,因为我不需要在使用框架的应用程序中链接或嵌入Google地图,并且一切正常.除非我在应用程式内使用Google地图.无论是在编译阶段获得“架构XY的未定义符号”,还是将GoogleMaps与之链接起来,然后在应用启动期间在调试控制台中收到警告墙,如:C

  10. ios – 在分组的表视图中混合静态和动态部分

    可能需要保持静电细胞的强大性能?在表视图的相同.xib文件中直接设计每个静态单元格,并为它们设置插座是否更好?(虽然这不允许重用我的自定义单元格设计…

随机推荐

  1. js中‘!.’是什么意思

  2. Vue如何指定不编译的文件夹和favicon.ico

    这篇文章主要介绍了Vue如何指定不编译的文件夹和favicon.ico,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  3. 基于JavaScript编写一个图片转PDF转换器

    本文为大家介绍了一个简单的 JavaScript 项目,可以将图片转换为 PDF 文件。你可以从本地选择任何一张图片,只需点击一下即可将其转换为 PDF 文件,感兴趣的可以动手尝试一下

  4. jquery点赞功能实现代码 点个赞吧!

    点赞功能很多地方都会出现,如何实现爱心点赞功能,这篇文章主要为大家详细介绍了jquery点赞功能实现代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  5. AngularJs上传前预览图片的实例代码

    使用AngularJs进行开发,在项目中,经常会遇到上传图片后,需在一旁预览图片内容,怎么实现这样的功能呢?今天小编给大家分享AugularJs上传前预览图片的实现代码,需要的朋友参考下吧

  6. JavaScript面向对象编程入门教程

    这篇文章主要介绍了JavaScript面向对象编程的相关概念,例如类、对象、属性、方法等面向对象的术语,并以实例讲解各种术语的使用,非常好的一篇面向对象入门教程,其它语言也可以参考哦

  7. jQuery中的通配符选择器使用总结

    通配符在控制input标签时相当好用,这里简单进行了jQuery中的通配符选择器使用总结,需要的朋友可以参考下

  8. javascript 动态调整图片尺寸实现代码

    在自己的网站上更新文章时一个比较常见的问题是:文章插图太宽,使整个网页都变形了。如果对每个插图都先进行缩放再插入的话,太麻烦了。

  9. jquery ajaxfileupload异步上传插件

    这篇文章主要为大家详细介绍了jquery ajaxfileupload异步上传插件,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  10. React学习之受控组件与数据共享实例分析

    这篇文章主要介绍了React学习之受控组件与数据共享,结合实例形式分析了React受控组件与组件间数据共享相关原理与使用技巧,需要的朋友可以参考下

返回
顶部