一般提起 Angular 的 Lazy Loading 時,第一個反應都是透過網址的方式來實作,但是否有方法可以借用 RouterModule
的幫助來產生 chunk 檔案,然後手動作載入的動作呢? 答案是可以的
環境設定
其實在 RouterModule
底層在執行 Lazy Loading 效果的方法,是藉由 NgModuleFactoryLoader 來完成的, 而底下有一個 subclass 叫做 SystemJsNgModuleLoader,這一個是我們所需要的 NgModuleLoader
,將其註冊在 module providers 區區塊內,所以 Module Loader 有了,那要怎麼註冊 module 成為可以延遲載入的 module 呢?
方法有兩個
-
使用
RouterModule.forChild(routes)
的方式註冊 modules1
2
3
4
5
6
7
8
9
10
11
12
13const routes: Route[] = [
{ loadChildren: 'app/lazy1/lazy1.module#Lazy1Module' },
{ loadChildren: 'app/lazy2/lazy2.module#Lazy2Module' }
];
({
declarations: [AppComponent],
imports: [BrowserModule, FormsModule, RouterModule.forChild(routes)],
providers: [SystemJsNgModuleLoader],
bootstrap: [AppComponent]
})
export class AppModule {} -
使用 provideRoutes 的方式註冊 modules
1
2
3
4
5
6
7
8
9
10
11
12const routes: Route[] = [
{ loadChildren: 'app/lazy1/lazy1.module#Lazy1Module' },
{ loadChildren: 'app/lazy2/lazy2.module#Lazy2Module' }
];
({
declarations: [AppComponent],
imports: [BrowserModule, FormsModule],
providers: [provideRoutes(routes), SystemJsNgModuleLoader],
bootstrap: [AppComponent]
})
export class AppModule {}
上述的兩種方式都可以達到一樣的效果
使用方式
constructor
環境設定好後,該如何使用呢? 這裡以 app.component.ts
為例,首先先在 constructor
載入 SystemJsNgModuleLoader
1 | constructor(private moduleLoader: SystemJsNgModuleLoader) {} |
template
範例我使用的 html 如下
1 | <button (click)="go('lazy1')">GO TO Lazy1</button> |
HTML說明
ngComponentOutlet
支援使用ngModuleFactory
的方法產生 Component,- 兩個按鈕都按下後,會將各 Module 所指定的
EntryComponent
顯示在ng-conainer
的地方
lazy module
其中一個 LazyModule
的程式碼如下
1 | ({ |
需要留意的是,由於我們會動態載入 component
,所以該 component
需要被註冊在 entryComponents
的區塊內。
另外於 Lazy1Module
的 區塊內設定一個 static property ,等一下在載入 module’s component 時會用到
go method
1 | const modules = { |
還記得在 constructor
所注入的 SystemJsNgModuleLoader
,該 class 只有一個 load
函式,這一個 load 函式接受一個引數,是用來指定要載入的 module 位置,這個位置會跟 AppModule
所設定的一樣。
1 | load(path: string): Promise<NgModuleFactory<any>> |
當成功載入時,會回傳一個 NgModuleFactory
,這個 NgModuleFactory
就可以直接指定給 ngComponentOutlet
使用,而 component 的部分,可以由剛剛所設定的靜態變數 entry 取得,一樣指定給 ngComponentOutlet
使用。
這樣子就完成手動載入 NgModule
的功能了,是不是很簡單!!!
延伸應用
Angular 在 Multi Page Application 的應用情境下,可以利用這樣的模式,動態的載入所需要的 NgModule
並啟用 Component
1 | ngDoBootstrap(appRef: ApplicationRef) { |
1 | <app-root data-module-path="./lazy1/lazy1.module#Lazy1Module"> |