iT邦幫忙

2022 iThome 鐵人賽

DAY 24
0

前言

大型網站應用程式專案由於功能較多,如果一開始就把全部的頁面或功能模組引入根模組,使用者體驗會因為初次進入畫面時間過久而降低,為了解決這個問題,延遲載入模組策略讓使用者不必在第一頁下載整個網站的所有模組、元件原始碼,有效降低初次進入畫面的讀取時間,大幅優化每個功能模組的載入速度。

然而對於網站應用程式中各別的大型功能模組而言,雖然一開始延遲載入降低了初次進入頁面的讀取時間,但每當使用者進入該功能模組的時候,還是可能會產生明顯的卡頓感,為了解決這個問題 Angular 路由模組進一步提供了預先載入(非同步載入)的機制,當使用者進入網站應用時,預先載入機制會透過非同步背景下載,在不影響畫面顯示或使用者操作的情況下完成功能模組載入。


延遲載入模組實作

製作一個功能模組 FeatureModule

src\app 中得到一個 FeatureModule

ng g m feature

然後建立一些範例頁面元件到 FeatureModule

ng g c hello
ng g c world

手動配置根模組延遲載入功能模組

src\app\app-routing.module.ts

...

// 路由設定
const routes: Routes = [
    {
        path: 'home', // 首頁元件路由
        component: HomeComponent,
        children: [

            ...

            // 延遲載入功能模組
            {
                path: 'feature',
                loadChildren: () => import('./feature/feature.module').then((m) => m.FeatureModule),
            },
        ],
    },

    ...
];

/**
 * ## 根模組路由設定
 *
 * @export
 * @class AppRoutingModule
 */
@NgModule({
    imports: [RouterModule.forRoot(routes)],
    exports: [RouterModule],
})
export class AppRoutingModule {}

手動配置功能模組

src\app\feature\feature.module.ts

...

// 引入功能模組路由設定
import { FeatureRoutingModule } from './feature-routing.module';

/**
 * ## 功能模組
 *
 * @export
 * @class FeatureModule
 */
@NgModule({
    declarations: [HelloComponent, WorldComponent],
    imports: [CommonModule, FeatureRoutingModule, ...NEBULAR_ALL],
})
export class FeatureModule {}

手動配置功能模組的路由設定

src\app\feature\feature-routing.module.ts

...

import { HelloComponent } from './hello/hello.component';
import { WorldComponent } from './world/world.component';

const FEATURE_ROUTES: Routes = [
    {
        path: 'hello',
        component: HelloComponent,
    },
    {
        path: 'world',
        component: WorldComponent,
    },
];

/**
 * ## 功能模組路由設定
 *
 * @export
 * @class FeatureRoutingModule
 */
@NgModule({
    imports: [RouterModule.forChild(FEATURE_ROUTES)],
    exports: [RouterModule],
})
export class FeatureRoutingModule {}

配置側邊菜單路由與多國語系

src\app\home\home.menuitem.ts

export const NbMenuItems = [

    ...

    {
        key: 'FEATURE.TITLE', // 對應 i18n [json檔] 的 key
        hidden: false,
        title: '功能模組頁面', // 頁面標題
        icon: { icon: 'award-outline', pack: 'eva' },
        expanded: true, // 預設展開
        children: [
            {
                key: 'FEATURE.HELLO', // 對應 i18n [json檔] 的 key
                hidden: false,
                title: '哈囉', // 頁面標題
                icon: { icon: 'award-outline', pack: 'eva' },
                link: '/home/feature/hello', // 路由連結
            },
            {
                key: 'FEATURE.WORLD', // 對應 i18n [json檔] 的 key
                hidden: false,
                title: '世界', // 頁面標題
                icon: { icon: 'award-outline', pack: 'eva' },
                link: '/home/feature/world', // 路由連結
            },
        ],
    },
];

成果畫面

g15

確認 FeatureModule 已經被延遲載入

打包時可在 console 看到結果。

npm run watch

or

ng build --watch --configuration development

p121

延遲載入模組前提是 AppModule 不能跟 FeatureModule 有任何相依關係


預先載入模組實作

預先載入全部可被延遲載入的模組

src\app\app-routing.module.ts

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { PreloadAllModules } from '@angular/router';    // 引入預先載入機制模組

...

// 路由設定
const routes: Routes = [
    ...
];

/**
 * ## 根模組路由設定
 *
 * @export
 * @class AppRoutingModule
 */
@NgModule({
    imports: [
        // 使用預先載入機制模組
        RouterModule.forRoot(routes, {
            preloadingStrategy: PreloadAllModules,  // 預先載入全部可被延遲載入的模組
        }),
    ],
    exports: [RouterModule],
})
export class AppRoutingModule {}

自訂的預先載入機制

當有些模組需要預先載入有些不需要的時候,我們也可以自訂 PreloadingStategy

ng g s selected-preloading

建立自訂的預先載入機制 SelectedPreloadingService
src\app\core\services\selected-preloading.service.ts

import { Injectable } from '@angular/core';
import { PreloadingStrategy, Route } from '@angular/router';
import { Observable, of } from 'rxjs';

/**
 * ## 自訂的預先載入機制
 *
 * @export
 * @class SelectedPreloadingService
 * @implements {PreloadingStrategy}
 */
@Injectable({
    providedIn: 'root',
})
export class SelectedPreloadingService implements PreloadingStrategy {
    preloadedModules: string[] = [];

    // route.data.preload 為 true 的模組執行預先載入
    preload(route: Route, load: () => Observable<any>): Observable<any> {
        if (route.data && route.data['preload']) return load();
        else return of(null);
    }
}

根模組引入自訂的預先載入機制

src\app\app-routing.module.ts

// 引入自訂的預先載入機制
import { SelectedPreloadingService } from '@core/services/selected-preloading.service';

...

// 路由設定
const routes: Routes = [
    {
        path: 'home', // 首頁元件路由
        component: HomeComponent,
        children: [

            ...

            // 延遲載入功能模組
            {
                path: 'feature',
                data: { preload: true }, // 設定為 true 時會啟動預先載入機制
                loadChildren: () => import('./feature/feature.module').then((m) => m.FeatureModule),
            },
        ],
    },

    ...
];

/**
 * ## 根模組路由設定
 *
 * @export
 * @class AppRoutingModule
 */
@NgModule({
    imports: [
        RouterModule.forRoot(routes, {
            preloadingStrategy: SelectedPreloadingService, // 使用自訂的預先載入機制
        }),
    ],
    providers: [SelectedPreloadingService], // 引入自訂的預先載入機制
    exports: [RouterModule],
})
export class AppRoutingModule {}

檢查載入機制是否有成功啟動的方式

要檢查延遲載入機制或者預先載入機制是否有成功啟動,只能在打包之後到瀏覽器運行中的 Network Panel 進行檢查,運行時打開 F12,我們看到預先載入的模組的確是 FeatureModule,如果沒有預先載入則要等到使用者訪問時才會讀取**(延遲載入機制)** FeatureModule 中的元件。

p122


結論

前端架構設計對於預先載入機制 preloadprefetch 的名詞會有一些混淆,兩者的作用都在於提早取得將來會用到的資源,差別在於,

  • Preload:取得當前頁面的資源。
  • prefetch:可跨越路由抓取不限於當前頁面的資源,類似記憶體預載分頁機制。

今天討論的 Angular 預先載入機制則是偏向於 prefetch 機制。

總結整理 Angular 的 模組載入機制

延遲載入

  • 使用 webpack code splitting 技術,將模組拆解成可延遲載入的程式片段 chunks
  • 當使用者訪問首頁,所有可延遲載入的 chunks 不會預先載入。
  • 當使用者訪問特定路由,延遲載入的模組才會即時載入。

預先載入

  • 使用 webpack code splitting 技術,將模組拆解成可延遲載入的程式片段 chunks
  • 當使用者訪問首頁,所有可延遲載入的 chunks (或透過設定為可預先載入的模組)會立即預先載入。
  • 預先載入的過程透過非同步背景下載,不影響畫面顯示或使用者操作。

my-app 還沒有實作登入模組,接下來將帶大家了解 Angular Router Guard 路由守衛 進而完成登入模組。


參考

@angular/router

Lazy-loading feature modules

Lazy Loading in Angular

PreloadingStrategy

Angular入門到精通系列教程

Angular 路由守衛

加上路由守衛


上一篇
Angular 路由模組
下一篇
路由守衛與登入模組實作
系列文
angular專案開發指南30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言