サーバーサイドレンダリングの実装
リスト1のコマンドを実行すると、図5に示すファイルが追加/変更されます。サーバーサイドレンダリングを実現するこれらの実装について、以下で概要を説明します。
サーバーサイドレンダリング対応で追加されるファイル
まず、追加されるファイルを表1に示します。サーバーサイドレンダリングの本質的な実装はNo.1~3で、No.4と5はビルド用の設定ファイルです。
| No. | ファイル名 | 役割 |
|---|---|---|
| 1 | app.server.module.ts | サーバーサイドのルートモジュール |
| 2 | main.server.ts | サーバーサイドのルートモジュールをエクスポートする実装 |
| 3 | server.ts | Express Webサーバーの実装 |
| 4 | webpack.server.config.ts | サーバーサイド実装のwebpack設定 |
| 5 | tsconfig.server.json | サーバーサイド実装のTypeScript設定 |
No.1のapp.server.module.tsは、サーバーサイドレンダリングされるWebページのルートモジュールです。
@NgModule({
imports: [
AppModule, // Webページのルートモジュール ...(1)
ServerModule, // サーバーサイドレンダリングのモジュール ...(2)
],
bootstrap: [AppComponent],
})
export class AppServerModule {}
元のWebページのルートモジュールAppModule(1)に加えて、サーバーサイドレンダリング機能を提供するServerModule(2)をインポートします。ここで定義したAppServerModuleクラスは、No.2のmain.server.tsで、リスト4の通りエクスポート(公開)されます。
export { AppServerModule } from './app/app.server.module';
No.3のserver.tsは、Node.js上で実行できるWebサーバー「Express」を利用してWebページをホストする実装です。Expressの詳細は公式ページも参考にしてください。サーバーサイドレンダリングに関連する実装をリスト5に示します。
app.engine('html', ngExpressEngine({ // ...(1)
bootstrap: AppServerModuleNgFactory, // ...(2)
providers: [
provideModuleMap(LAZY_MODULE_MAP) // ...(3)
]
}));
(1)の「app.engine」は、Webページを生成するプログラム(テンプレートエンジン)を指定するExpressのメソッドです。ここで、Angular Universalが提供するテンプレートエンジンngExpressEngineを指定すると、ExpressでAngularのサーバーサイドレンダリングを利用できるようになります。
ngExpressEngineのbootstrap引数(2)には、リスト3、4で実装したAppServerModule(がビルド時に変換されるAppServerModuleNgFactory)を指定します。providers引数には、サーバーサイドで利用するモジュールやサービスなどの設定を記述します。
[補足]モジュールの遅延ロードとサーバーサイドレンダリング
リスト5(3)は、クライアントサイド実行時にルーターの機能で遅延ロードされるモジュールを、サーバーサイドで実行できるようにする設定です。モジュールの遅延ロードについては、公式ドキュメントも参考にしてください。
この設定を有効にする場合、リスト3のAppServerModuleで、ModuleMapLoaderModuleをインポートします。記事執筆時点では、リスト1のコマンドで生成するapp.server.module.tsにインポート記述が含まれないため、手動で追加する必要があります。
imports: [ AppModule, ServerModule, ModuleMapLoaderModule // 追加でインポート ],
遅延ロードされるモジュールがない場合は、リスト5(3)やリスト6の記述がなくても、正常にサーバーサイドレンダリングが動作します。
[補足]ngExpressEngineの内部で実行されるrenderModuleFactoryメソッド
リスト5(1)のngExpressEngineは、Angularでサーバーサイドレンダリングを実行してHTML文字列を生成するrenderModuleFactoryメソッドを内部で実行します。renderModuleFactoryメソッドの詳細は、Angularの公式ドキュメントを参照してください。
サーバーサイドレンダリング対応で変更されるファイル
次に、変更されるファイルを表2に示します。以下ではポイントとなる変更点を抽出して説明していきます。詳細はサンプルコードを参照してください。
| No. | ファイル名 | 変更内容 |
|---|---|---|
| 1 | app.module.ts | BrowserModuleの設定変更(後述) |
| 2 | package.json | パッケージ追加、スクリプト定義追加(後述) |
| 3 | package-lock.json | パッケージ追加 |
| 4 | angular.json | サーバーサイド実装のビルド設定追加 |
| 5 | main.ts | クライアントサイド処理の開始タイミングをページロード後(DOMContentLoadedイベント発生時)に変更 |
No.1のapp.module.tsは、クライアントサイドで実行されるWebページのルートモジュールです。BrowserModuleにwithServerTransitionメソッドを追加して、サーバーサイドレンダリングのページからクライアントサイドの(通常の)ページに切り替える指定を追加します。「appId: 'serverApp'」はWebページを一意に識別するIDの指定です。
@NgModule({
(略)
imports: [
// BrowserModule // 変更前
BrowserModule.withServerTransition({ appId: 'serverApp' }) // 変更後
],
(略)
})
export class AppModule { }
また、No.2のpackage.jsonには、サーバーサイドレンダリングに関連した表3のスクリプト定義が追加されます。「npm run <スクリプトのキー>」コマンドで実行できます。
| No. | スクリプトのキー | 処理内容 |
|---|---|---|
| 1 | build:ssr | サーバーサイドレンダリング用のビルド |
| 2 | serve:ssr | WebページをホストするWebサーバーを起動 |
| 3 | build:client-and-server-bundles | クライアントとサーバーのファイルをビルド |
| 4 | compile:server Express | Webサーバーの実装をビルド |
サーバーサイドレンダリングを有効にしてアプリを実行するには、No.1のスクリプトを実行してビルド後、No.2のスクリプトでWebサーバーを起動します。No.1のビルド処理は、内部的にNo.3とNo.4を呼び出して、クライアント/サーバーのアプリファイルとWebサーバーの実装をすべてビルドするようになっています。
ビルドされたファイルは、distフォルダー配下に、表4の通り出力されます。
| No. | 出力先 | 内容 |
|---|---|---|
| 1 | dist/browser | クライアントサイドで実行されるファイル群 |
| 2 | dist/server | サーバーサイドで実行されるファイル群 |
| 3 | dist/server.ts | ExpressでWebサーバーを実行するファイル |
