ルートレベルのコード分割を使用して、アプリのパフォーマンスを改善しましょう。
この記事では、Angular アプリケーションでルートレベルのコード分割を設定する方法について説明します。これにより、JavaScript バンドルのサイズを削減し、インタラクティブになるまでの時間を大幅に短縮できます。
この記事のコードサンプルは GitHub で入手できます。エアガール ルーティングの例は、eager ブランチで確認できます。ルートレベルのコード分割の例は、遅延ブランチにあります。
コード分割が重要である理由
ウェブ アプリケーションの複雑さはますます増大しており、ユーザーに送信される JavaScript の量も大幅に増加しています。JavaScript ファイルのサイズが大きいと、インタラクティビティが著しく遅れることがあるため、特にモバイルでは高額なリソースになる可能性があります。
アプリの機能を犠牲にすることなく JavaScript バンドルを圧縮する最も効率的な方法は、積極的なコード分割を導入することです。
コード分割を使用すると、アプリケーションの JavaScript を、異なるルートまたは機能に関連付けられた複数のチャンクに分割できます。この方法では、ユーザーが必要とする JavaScript を最初のアプリケーション読み込み時にのみ送信するため、読み込み時間が短縮されます。
コード分割手法
コード分割は、コンポーネント レベルとルートレベルの 2 つのレベルで行うことができます。
- コンポーネントレベルのコード分割では、コンポーネントを独自の JavaScript チャンクに移動し、必要に応じて遅延読み込みします。
- ルートレベルのコード分割では、各ルートの機能を個別のチャンクにカプセル化します。ユーザーがアプリケーションを操作すると、個々のルートに関連付けられたチャンクを取得し、必要に応じて関連する機能を取得します。
この記事では、Angular でルートレベルの分割を設定する方法について説明します。
サンプル アプリケーション
Angular でルートレベルのコード分割を使用する方法を詳しく説明する前に、サンプルアプリを見てみましょう。
アプリのモジュールの実装を確認します。AppModule
内には、HomeComponent
に関連付けられたデフォルト ルートと、NyanComponent
に関連付けられた nyan
ルートの 2 つのルートが定義されています。
@NgModule({
...
imports: [
BrowserModule,
RouterModule.forRoot([
{
path: '',
component: HomeComponent,
pathMatch: 'full'
},
{
path: 'nyan',
component: NyanComponent
}
])
],
...
})
export class AppModule {}
ルートレベルのコード分割
コード分割を設定するには、nyan
イージールートをリファクタリングする必要があります。
Angular CLI バージョン 8.1.0 では、次のコマンドですべての操作を実行できます。
ng g module nyan --module app --route nyan
これにより、次が生成されます。
- NyanModule
という新しいルーティング モジュール
- NyanModule
を動的に読み込む nyan
という AppModule
内のルート
- NyanModule
内のデフォルト ルート
- ユーザーがデフォルト ルートにアクセスしたときにレンダリングされる NyanComponent
というコンポーネント
Angular でコード分割を実装する手順を手動で確認して、理解を深めましょう。
ユーザーが nyan
ルートに移動すると、ルーターはルーター アウトレットに NyanComponent
をレンダリングします。
Angular でルートレベルのコード分割を使用するには、ルート宣言の loadChildren
プロパティを設定し、動的インポートと組み合わせます。
{
path: 'nyan',
loadChildren: () => import('./nyan/nyan.module').then(m => m.NyanModule)
}
エアガール ルートとの主な違いは次の 2 つです。
component
ではなくloadChildren
を設定しています。ルートレベルのコード分割を使用する場合は、コンポーネントではなく、動的に読み込まれるモジュールを参照する必要があります。loadChildren
では、Promise が解決されたら、NyanComponent
を指すのではなくNyanModule
を返します。
上記のスニペットは、ユーザーが nyan
に移動したときに、Angular が nyan
ディレクトリから nyan.module
を動的に読み込み、モジュールで宣言されたデフォルト ルートに関連付けられたコンポーネントをレンダリングすることを指定しています。
デフォルト ルートをコンポーネントに関連付けるには、次の宣言を使用します。
import { NgModule } from '@angular/core';
import { NyanComponent } from './nyan.component';
import { RouterModule } from '@angular/router';
@NgModule({
declarations: [NyanComponent],
imports: [
RouterModule.forChild([{
path: '',
pathMatch: 'full',
component: NyanComponent
}])
]
})
export class NyanModule {}
このコードは、ユーザーが https://example.com/nyan
に移動したときに NyanComponent
をレンダリングします。
Angular ルーターがローカル環境で nyan.module
を遅延ダウンロードすることを確認するには:
- Ctrl+Shift+J(Mac の場合は Command+Option+J)キーを押して DevTools を開きます。
[ネットワーク] タブをクリックします。
サンプルアプリで [NYAN] をクリックします。
nyan-nyan-module.js
ファイルがネットワーク タブに表示されます。
この例は GitHub で確認できます。
スピナーを表示する
現時点では、ユーザーが [NYAN] ボタンをクリックしても、バックグラウンドで JavaScript が読み込まれていることがアプリケーションに表示されません。スクリプトの読み込み中にユーザーにフィードバックを提供するには、スピナーを追加することをおすすめします。
そのためには、まず app.component.html
の router-outlet
要素内にインジケーターのマークアップを追加します。
<router-outlet>
<span class="loader" *ngIf="loading"></span>
</router-outlet>
次に、ルーティング イベントを処理する AppComponent
クラスを追加します。このクラスは、RouteConfigLoadStart
イベントを検出すると loading
フラグを true
に設定し、RouteConfigLoadEnd
イベントを検出するとフラグを false
に設定します。
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
loading: boolean;
constructor(router: Router) {
this.loading = false;
router.events.subscribe(
(event: RouterEvent): void => {
if (event instanceof NavigationStart) {
this.loading = true;
} else if (event instanceof NavigationEnd) {
this.loading = false;
}
}
);
}
}
以下の例では、スピナーが動作している様子を確認できるように、500 ミリ秒のレイテンシを人為的に導入しています。
まとめ
ルートレベルのコード分割を適用すると、Angular アプリケーションのバンドルサイズを縮小できます。
- Angular CLI の遅延読み込みモジュール ジェネレータを使用して、動的に読み込まれるルートを自動的にスキャフォールドします。
- ユーザーがレイジー ルートに移動したときに読み込みインジケーターを追加して、進行中のアクションがあることを示します。