不通過路由的情況下, 怎麼懶加載一個angular模組,並動態創建其中聲明的組件?以下這篇文章跟大家介紹一下方法,希望對大家有幫助!
環境: Angular 13.x.x
angular中支援可以透過路由來懶載入某些頁面模組已達到減少首螢幕尺寸, 提高首屏載入速度的目的. 但是這種通過路由的方式有時候是無法滿足需求的。 【相關教學推薦:《angularjs影片教學》】
例如, 點擊一個按鈕後顯示一行工具列, 這個工具列元件我不希望它預設會打包進main. js
, 而是用戶點按鈕後動態把元件載入並顯示出來.
那為什麼要動態載入呢? 如果直接在目標頁面元件引入工具列元件, 那麼工具列元件中的程式碼就會被打包進目標頁面元件所在的模組, 這會導致目標頁面元件所在的模組產生的js體積變大; 透過動態懶載入的方式, 可以讓工具列元件只在使用者點了按鈕後再載入,這樣就可以達到減少首屏尺寸的目的.
為了演示, 新建一個angular專案, 然後再新建一個ToolbarModule
, 專案的目錄結構如圖
#為了達到演示的目的, 我在ToolbarModule
的html模板中放了個將近1m的base64圖片, 然後直接在AppModule
中引用ToolbarModule
, 然後執行ng build
, 執行結果如圖
可以看到打包尺寸到達了1.42mb
, 也就是說使用者每次刷新這個頁面, 不管使用者有沒有點擊顯示工具列按鈕, 工具列元件相關的內容都會被載入出來, 這造成了資源的浪費, 所以下面將ToolbarModule
從AppModule
的imports
聲明中移除, 然後在用戶點擊首次點擊顯示時懶加載工具列組件.
首先, 新建一個ToolbarModule
和ToolbarComponent
, 並在ToolbarModule
聲明ToolbarComponent
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 };
import { Component, OnInit } from '@angular/core'; @Component({ selector: 'toolbar', templateUrl: './toolbar.component.html', styles: [ ` svg { width: 64px; height: 64px; } 聊聊Angular中懶加載模組並動態顯示它的元件 { width: 64px; height: 64px; object-fit: cover; } `, ], }) export class ToolbarComponent implements OnInit { constructor() {} ngOnInit(): void {} }
<p 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> <聊聊Angular中懶加載模組並動態顯示它的元件 src="<这里应该是一张大小将近1M的base64图片, 内容较大, 就略去了...>" alt=""> </p>
然後再AppComponent
的中按鈕點擊事件處理程序中寫入載入工具列模組的程式碼:
import { Component, createNgModuleRef, Injector, ViewChild, ViewContainerRef } from '@angular/core'; @Component({ selector: 'root', template: ` <p class="container h-screen flex items-center flex-col w-100 justify-center"> <p class="mb-3" [ngClass]="{ hidden: !isToolbarVisible }"> <ng-container #toolbar></ng-container> </p> <p> <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> </p> </p> `, }) 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
打包的尺寸大小
可以看到首屏尺寸沒有開頭那麼離譜了, 原因是沒有在AppModule
和AppComponent
直接導入ToolbarModule
和ToolbarComponent
, ToolbarModule
被打進了另外的js檔案中(Lazy Chunk Files), 當首次點擊顯示
按鈕時, 就會載入這個包含 ToolbarModule
的js檔案
注意看下面的gif示範中, 首次點擊顯示
按鈕, 瀏覽器網路偵錯工具中會多出一個對src_app_toolbar_toolbar_module_ts.js
檔案的請求
更多程式相關知識,請造訪:程式設計影片! !
以上是聊聊Angular中懶加載模組並動態顯示它的元件的詳細內容。更多資訊請關注PHP中文網其他相關文章!