Angular中懶載入一個模塊並動態創建顯示該模塊下聲明的組件

来源:https://www.cnblogs.com/laggage/archive/2022/05/16/16275367.html
-Advertisement-
Play Games

不通過路由的情況下, 懶載入一個angular模塊, 並動態創建其中聲明的組件 ...


環境: Angular 13.x.x

angular中支持可以通過路由來懶載入某些頁面模塊已達到減少首屏尺寸, 提高首屏載入速度的目的. 但是這種通過路由的方式有時候是無法滿足需求的.

比如, 點擊一個按鈕後顯示一行工具欄, 這個工具欄組件我不希望它預設打包進main.js, 而是用戶點按鈕後動態把組件載入並顯示出來.

那為什麼要動態載入呢? 如果直接在目標頁面組件引入工具欄組件, 那麼工具欄組件中的代碼就會被打包進目標頁面組件所在的模塊, 這會導致目標頁面組件所在的模塊生成的js體積變大; 通過動態懶載入的方式, 可以讓工具欄組件只在用戶點了按鈕後再載入, 這樣就可以達到減少首屏尺寸的目的.

為了演示, 新建一個angular項目, 然後再新建一個ToolbarModule, 項目的目錄結構如圖

img

為了達到演示的目的, 我在ToolbarModule的html模板中放了個將近1m的base64圖片, 然後直接在AppModule中引用ToolbarModule, 然後執行ng build, 執行結果如圖

img

可以看到打包尺寸到達了1.42mb, 也就是說用戶每次刷新這個頁面, 不管用戶有沒有點擊顯示工具欄按鈕, 工具欄組件相關的內容都會被載入出來, 這造成了資源的浪費, 所以下麵將ToolbarModuleAppModuleimports聲明中移除, 然後在用戶點擊首次點擊顯示時懶載入工具欄組件.

懶載入工具欄組件

首先, 新建一個ToolbarModuleToolbarComponent, 併在ToolbarModule聲明ToolbarComponent

img

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打包的尺寸大小

img

可以看到首屏尺寸沒有開頭那麼離譜了, 原因是沒有在AppModuleAppComponent直接導入ToolbarModuleToolbarComponent, ToolbarModule被打進了另外的js文件中(Lazy Chunk Files), 當首次點擊顯示按鈕時, 就會載入這個包含ToolbarModule的js文件

註意看下麵的gif演示中, 首次點擊顯示按鈕, 瀏覽器網路調試工具中會多出一個對src_app_toolbar_toolbar_module_ts.js文件的請求

img

作者:Laggage

出處:https://www.cnblogs.com/laggage/p/16275367.html

說明:轉載請註明來源


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Environment Modules: provides dynamic modification of a user's environment 一個可以動態修改用戶環境的工具 ...
  • 待持續更新 一、批處理命令 1.命令 使用help命令查看結果集 結果集 1. echo 和 @ 回顯命令 @ #關閉單行回顯 echo off #從下一行開始關閉回顯 @echo off #從本行開始關閉回顯。一般批處理第一行都是這個 echo on #從下一行開始打開回顯 echo #顯示當前是 ...
  • 一、概述 1)什麼是任務調度? 大數據平臺技術框架支持的開發語言多種多樣,開發人員的背景差異也很大,這就產生出很多不同類型的程式(任務)運行在大數據平臺之上,如:MapReduce、Hive、Pig、Spark、Java、Shell、Python 等。 這些任務需要不同的運行環境,並且除了定時運行, ...
  • 資料庫版本標準化 1.確認Supported Platforms https://www.mysql.com/support/ 2.確認安裝版本 推薦:5.7.22 ,8.0.20以後的雙數GA版本 3.獲取MySQL軟體包 https://downloads.mysql.com/archives/ ...
  • 高可用性(英語:high availability,縮寫為 HA) IT術語,指系統無中斷地執行其功能的能力,代表系統的可用性程度。是進行系統設計時的準則之一。 高可用性系統意味著系統服務可以更長時間運行,通常通過提高系統的容錯能力來實現。高可用性或者高可靠度的系統不會希望有單點故障造成整體故障的情 ...
  • 1.將軟體包kafka_2.12-3.0.0.tgz上傳到/opt/software/目錄下,解壓到/opt/module/目錄下 tar -zxvf kafka_2.12-3.0.0.tgz -C /opt/module/ 2.修改解壓後的文件名稱 mv kafka_2.12-3.0.0/ kaf ...
  • 1.拷貝 apache-zookeeper-3.5.7-bin.tar.gz到/opt/software目錄下 2.解壓到指定目錄 tar -zxvf apache-zookeeper-3.5.7-bin.tar.gz -C /opt/module/ 3.修改名稱 cd /opt/module mv ...
  • 華為分析服務面向開發者提供兩種數據展現方式:(1)事件數據下載,開發者可以將事件數據下載後導入到自有的分析系統中;(2)直接在AGC概覽頁面查看用戶數和事件數。 問題描述 某開發者想將事件數據導入到自有系統中,他在導出數據時將標識用戶方式選擇“按UserID和設備”,最後發現導出數據中的事件數比分析 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...