AngularはGoogleが開発したフロントエンドのJavaScriptフレームワークで、スケーラブルなエンタープライズグレードのウェブアプリケーションを構築するのに有用です。これを使って開発するアプリケーションの中には、当然、規模が大きく読み込みの面で対策が必要になるものもあります。

読み込み時間を短縮し、ユーザーの全体的な体験を改善するには、遅延読み込みとして知られる技術を使用することができます。Angularネイティブの遅延読み込み機能を使って、ウェブアプリケーションに必要な部位のみを最初に読み込み、その後必要に応じて他のモジュールに移行することができます。

この記事では、遅延読み込みについて、そしてそれがどのようにウェブアプリのスピードアップに役立つのかをご紹介します。

遅延読み込みとは

遅延読み込みとは、簡単に言えばウェブページの要素を必要なタイミングで読み込むテクニックのことです。これと対になるのがイーガーローディングで、すべての要素がまとめて読み込まれ(またはそれを試行し)ます。すべての画像、動画、CSS、JavaScriptのコードを区別することなく読み込むと、読み込み時間が長くなり、ユーザーにとっての使い心地が悪くなってしまいます。

遅延読み込みは、多くのコンテンツを扱うサイトにおいて、画像や動画によく用いられます。すべてのメディアを一度に読み込む代わりに、(例えばウェブサイトであれば)ページをスクロールするタイミングで、それぞれの要素を読み込んでいくことになります。

Angularはシングルページのアプリケーションフレームワークで、機能の多くをJavaScriptに依存しています。アプリが大きくなるとJavaScriptのコードが肥大化し、それに伴いデータ使用量と読み込み時間が増加することがあります。そこで遅延読み込みを使用することで、必要なモジュールを最初に取得し、他のモジュールの読み込みを必要な時まで延期し、処理時間を短縮することができます。

Angularにおける遅延読み込みのメリット

遅延読み込みにより、サイトをユーザーフレンドリーなものに変えることができます。実際には、以下のようなメリットが挙げられます。

  • 表示時間の短縮:JavaScriptにはページ表示やデータ読み込みの命令が含まれます。このため、レンダーブロッキングリソースとなります。つまり、ブラウザはページのレンダリング完了前に、すべてのJavaScriptの読み込みを待機しなければなりません。Angularで遅延読み込みを行うと、JavaScriptは細かなかたまりに分割され、別々に読み込まれます。最初のかたまりには、ページのメインモジュールに必要なロジックだけが組み込まれます。これが先に処理され、その後残りのモジュールが(遅延の上で)読み込まれます。最初に処理するかたまりのサイズを小さくすることで、サイトの読み込みとレンダリングを高速化できます。
  • データ使用量の削減:データを分割し、必要に応じて都度読み込むことで、使用する帯域幅が少なくなる可能性があります。
  • ブラウザリソースの節約:ブラウザに必要な部位だけ読み込まれるので、不要なコードを解釈してレンダリングするのにメモリやCPUを浪費する必要がなくなります。

Angularで遅延読み込みを実装する

ここからの説明を読み進めるには、以下の知識や準備が必要になります。

  • Node.jsのインストール
  • Angularの基本的な知識

プロジェクトを作成する

Angular CLIを使用してプロジェクトを作成します。CLIはnpmを使用してコマンドを実行することでインストールできます。

npm install -g @angular/cli

その後、以下のようにLazy Loading Demoという名前のプロジェクトを作成します。

ng new lazy-loading-demo --routing

このコマンドでルーティングを含む新しいAngularプロジェクトが作成されます。アプリのコードがあるのはsrc/appフォルダですので、ここで作業を行うことになります。このフォルダ内には、メインのルーティングファイルであるapp-routing.module.tsが格納されています。フォルダの構造は次のようになっています。

Angularプロジェクトのフォルダ構造
Angularプロジェクトのフォルダ構造

ルーティングを使ったフィーチャーモジュールの作成

次に、遅延読み込み機能を担当するモジュールを作成します。このモジュールを作成するには、以下のコマンドを実行します。

ng generate module blog --route blog --module app.module

このコマンドにより、ルーティングとあわせてBlogModuleという名前のモジュールが作成されます。src/app/app-routing.module.tsを開くと、次のような画面が表示されます。

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

const routes: Routes = [ { path: 'blog', loadChildren: () => import('./blog/blog.module').then(m => m.BlogModule) }];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { } 

遅延読み込みで重要なのは、3行目の部分です。

const routes: Routes = [ { path: 'blog', loadChildren: () => import('./blog/blog.module').then(m => m.BlogModule) }];

この行でルートを定義しています。ブログのルートでcomponentの代わりにloadChildren引数が使われます。loadChildren引数はAngularにルートの遅延読み込み、つまりルートにアクセスしたときだけ動的にモジュールをインポートし、その後ルーターに戻すように指示しています。モジュールはrouting.module.tsファイルでblog/**のような独自の子ルートを定義することになります。生成されるblogモジュールは次の通りです。

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { BlogComponent } from './blog.component';

const routes: Routes = [{ path: '', component: BlogComponent }];

@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule]
})
export class BlogRoutingModule { }

このルーティングファイルでは、''というルートが定義されています。このルートは/blogを処理し、BlogComponentを指しています。さらにコンポーネントを追加して、このファイルでそのルートを定義することもできます。

たとえば、特定のブログ記事についての情報を取得するコンポーネントを追加したい場合には、次のコマンドでコンポーネントを作成できます。

ng generate component blog/detail

これによりブログの詳細情報を扱うコンポーネントが生成され、blogモジュールに追加されます。このルートを追加するには、次のように配列に加えるだけでOKです。

const routes: Routes = [{ path: '', component: BlogComponent },
                        {path:"/:title",component: DetailComponent}];

これによりblog/:titleを解決するルートが追加されます(例:blog/angular-tutorial)。このルートの配列は遅延読み込みされ、一番最初に読み込まれる部位からは除外されます。

遅延読み込みの確認

ng serveを実行して出力を観察すれば、遅延読み込みが機能していることを簡単に確認することができます。出力の一番下には、次のようなものが表示されるはずです。

Angularの<code>ng serve</code>を使って遅延読み込みを検証する
Angularのng serveを使って遅延読み込みを検証する

上の出力は2つの部位に分かれています。Initial Chunk Filesは、ページが最初にロードされたときに読み込み対象となったファイルです。Lazy Chunk Filesは、遅延読み込みされたファイルです。この例では、blogモジュールが遅延されていることがわかります。

ブラウザのネットワークログから遅延読み込みを確認する

遅延読み込みを確認するもう一つの方法として、ブラウザの開発者ツールパネルにある「ネットワーク」タブを使うことができます。(Windowsの場合、ChromeとMicrosoft Edgeでは「F12」、Firefoxでは「Ctrl-Shift-I」です。Macでは、Chrome、Firefox、Safariの「Command-Option -I」です)。

ネットワーク経由で読み込まれたJavaScriptファイルのみを表示するには、「JS 」フィルタを選択します。アプリの初期ロード後には、次のような画面が表示されるはずです。

デベロッパーツールでJavaScriptダウンロードのログを表示する
デベロッパーツールでJavaScriptダウンロードのログを表示する

/blogに移動すると、新しいかたまりとしてsrc_app_blog_blog_module_ts.jsが読み込まれていることがわかります。つまり、このモジュールが、該当するルートに移動したときにのみ要求されている(遅延読み込みされている)ということです。ネットワークログは次のような見た目にはずです。

モジュールが遅延読み込みされていることが確認できる
モジュールが遅延読み込みされていることが確認できる

遅延を利用することの意味

比較のために、読み込みを遅延しないモジュールも作成し、ファイルサイズと読み込み時間にどのような影響が出るか見てみましょう。これを実証するために、今回は認証用のモジュールを作成します。このようなモジュールは(認証が基本的にすべてのユーザーに要求されることを考えると)通常通り読み込むのがセオリーだと言えるでしょう。

CLIで次のコマンドを実行して、AuthModuleを作成します。

ng generate module auth --routing --module app.module

これにより、モジュールとルーティングファイルが生成されます。また、app.module.tsファイルにモジュールが追加されます。しかし、前回モジュールを生成するのに使用したコマンドとは異なり、今回のコマンドでは遅延読み込みのルートは追加していません。--route <name>の代わりに--routingパラメータを使用します。これにより、app.module.tsにあるimportsの配列に認証モジュールが追加されます。

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    AuthModule //認証モジュールを追加
  ],
  providers: [],
  bootstrap: [AppComponent]
})

AppModule内のimportsの配列にAuthModuleを追加したことで、認証モジュールが初回読み込み時に(メインのJavaScriptバンドルとして)読み込まれることになります。この挙動を確認するために、ng serveを再度実行して、出力を確認してみましょう。

認証モジュールを追加してからAngularの<code>ng serve</code>コマンドを実行した結果
認証モジュールを追加してからAngularのng serveコマンドを実行した結果

ご覧の通り、認証モジュールは遅延読み込みされていません。初回に読み込まれるバンドルのサイズが大きくなっていることがわかります。main.jsファイルのサイズはほぼ2倍(8KBから15KB)に増加ししています。この例では、コンポーネントにあまりコードが含まれていないため、増加量は小さくなっています。しかし、コンポーネントにロジックを詰め込むと、このファイルサイズはさらに大きくなり、遅延読み込みをすることの効果は大きなものになります。

まとめ

今回の記事では、Angularで遅延読み込みを使い、必要な時だけモジュールを取得する方法を学びました。これは読み込みにかかる時間を改善し、データ使用量を減らし、フロントエンドとバックエンドのリソースを効率よく活用するのに便利なテクニックです。

遅延読み込みは、コンテンツデリバリネットワークJavaScriptコードの圧縮などの技術とともに、ウェブサイトのパフォーマンスとユーザーの満足度の両方を引き上げることができます。

WordPressサイトの開発や運用に際して根本的なスピードアップをお求めであれば、Kinstaのエッジキャッシュがおすすめです。

Michael Nyamande

デジタルプロダクトマネージャー。技術に目がなく、常に違う技術を追いかけている。ウェブ/モバイルフレームワーク、ノーコード開発、ブロックチェーン開発に関心あり。