サーバーサイドレンダリング (SSR) は、サーバーでページをレンダリングするプロセスです。これにより、初期のページ状態を含む初期HTMLコンテンツが生成されます。HTMLコンテンツがブラウザに配信されると、Angularはアプリケーションを初期化し、HTMLに含まれるデータを利用します。
SSR を使用する理由
SSRはクライアントサイドレンダリング (CSR) に比べて、主に以下の利点があります。
- パフォーマンスの向上: SSRは、完全にレンダリングされたHTMLをクライアントに配信することで、Webアプリケーションのパフォーマンスを向上させることができます。これにより、ブラウザはアプリケーションのJavaScriptをダウンロードする前に、HTMLを解析して表示できます。これは、帯域幅の狭い接続やモバイルデバイスのユーザーにとって特に有利です。
- Core Web Vitals の向上: SSRは、Core Web Vitals (CWV) 統計を使用して測定できるパフォーマンスの向上をもたらします。これには、最初のコンテンツフルペイント (FCP) や最大のコンテンツフルペイント (LCP)、累積レイアウトシフト (CLS) などの指標の改善が含まれます。
- SEO の向上: SSRは、検索エンジンがアプリケーションのコンテンツをクロールしてインデックス付けしやすくすることで、Webアプリケーションの検索エンジン最適化 (SEO) を向上させることができます。
サーバーサイドレンダリングを有効にする
SSRを使用して新規プロジェクトを作成するには、次のコマンドを実行します。
ng new --ssr
既存のプロジェクトにSSRを追加するには、Angular CLIの ng add
コマンドを使用します。
ng add @angular/ssr
Note: Angularにおける最新のSSRの進化に興味がありますか?開発者プレビューのハイブリッドレンダリングAPIを覗いてみてください。
これらのコマンドは、SSRを有効にするためのアプリケーションコードを作成および更新し、プロジェクト構造に余分なファイルを追加します。
my-app
|-- server.ts # アプリケーションサーバー
└── src
|-- app
| └── app.config.server.ts # サーバーアプリケーション構成
└── main.server.ts # メインサーバーアプリケーションのブートストラップ
アプリケーションがサーバーサイドでレンダリングされていることを確認するには、ng serve
でローカルに実行します。最初のHTMLリクエストには、アプリケーションのコンテンツが含まれている必要があります。
サーバーサイドレンダリングの構成
Note: In Angular v17 and later, server.ts
is no longer used by ng serve
. The dev server will use main.server.ts
directly to perfom server side rendering.
server.ts
ファイルは、Node.js ExpressサーバーとAngularサーバーサイドレンダリングを構成します。CommonEngine
はAngularアプリケーションをレンダリングするために使用されます。
import {APP_BASE_HREF} from '@angular/common';import {CommonEngine} from '@angular/ssr/node';import express from 'express';import {fileURLToPath} from 'node:url';import {dirname, join, resolve} from 'node:path';import bootstrap from './src/main.server';// The Express app is exported so that it can be used by serverless Functions.export function app(): express.Express { const server = express(); const serverDistFolder = dirname(fileURLToPath(import.meta.url)); const browserDistFolder = resolve(serverDistFolder, '../browser'); const indexHtml = join(serverDistFolder, 'index.server.html'); const commonEngine = new CommonEngine(); server.set('view engine', 'html'); server.set('views', browserDistFolder); // TODO: implement data requests securely // Serve data from URLS that begin "/api/" server.get('/api/**', (req, res) => { res.status(404).send('data requests are not yet supported'); }); // Serve static files from /browser server.get( '*.*', express.static(browserDistFolder, { maxAge: '1y', }), ); // All regular routes use the Angular engine server.get('*', (req, res, next) => { const {protocol, originalUrl, baseUrl, headers} = req; commonEngine .render({ bootstrap, documentFilePath: indexHtml, url: `${protocol}://${headers.host}${originalUrl}`, publicPath: browserDistFolder, providers: [{provide: APP_BASE_HREF, useValue: req.baseUrl}], }) .then((html) => res.send(html)) .catch((err) => next(err)); }); return server;}function run(): void { const port = process.env['PORT'] || 4000; // Start up the Node server const server = app(); server.listen(port, () => { console.log(`Node Express server listening on http://localhost:${port}`); });}run();
Angular CLIは、Angularアプリケーションのサーバーサイドレンダリングに焦点を当てた、初期のサーバー実装をスキャフォールディングします。このサーバーは、APIルート、リダイレクト、静的アセットなど、他の機能をサポートするように拡張できます。詳細については、Express ドキュメント を参照してください。
APIの詳細については、CommonEngine
APIリファレンスを参照してください。
ハイドレーション
ハイドレーションは、クライアント上でサーバーサイドレンダリングされたアプリケーションを復元するプロセスです。これには、サーバーでレンダリングされたDOM構造の再利用、アプリケーション状態の永続化、サーバーが既に取得したアプリケーションデータの転送、その他のプロセスが含まれます。ハイドレーションは、SSRを使用する場合、デフォルトで有効になります。ハイドレーションの詳細については、ハイドレーションガイド を参照してください。
HttpClient を使用する場合のデータキャッシュ
HttpClient
は、サーバーで実行されているときに、送信されるネットワークリクエストをキャッシュします。この情報はシリアル化され、サーバーから送信される最初のHTMLの一部としてブラウザに転送されます。ブラウザでは、HttpClient
はキャッシュにデータが存在するかどうかをチェックし、存在する場合は初期アプリケーションレンダリング中に新しいHTTPリクエストを行う代わりに、キャッシュされたデータを使用します。HttpClient
は、ブラウザで実行されているアプリケーションが安定すると、キャッシュの使用を停止します。
デフォルトでは、HttpClient
は、Authorization
または Proxy-Authorization
ヘッダーが含まれていないすべての HEAD
および GET
リクエストをキャッシュします。これらの設定は、ハイドレーションを提供するときに withHttpTransferCacheOptions
を使用することでオーバーライドできます。
bootstrapApplication(AppComponent, { providers: [ provideClientHydration(withHttpTransferCacheOptions({ includePostRequests: true })) ]});
サーバー対応コンポーネントのオーサリング
一部の一般的なブラウザAPIや機能は、サーバーでは使用できない場合があります。アプリケーションは、window
、document
、navigator
、location
などのブラウザ固有のグローバルオブジェクト、および HTMLElement
の特定のプロパティを使用できません。
一般的に、ブラウザ固有のシンボルに依存するコードは、サーバーではなく、ブラウザでのみ実行する必要があります。これは、afterRender
および afterNextRender
ライフサイクルフックを使用して強制できます。これらは、ブラウザでのみ実行され、サーバーではスキップされます。
import { Component, ViewChild, afterNextRender } from '@angular/core';@Component({ selector: 'my-cmp', template: `<span #content>{{ ... }}</span>`,})export class MyComponent { @ViewChild('content') contentRef: ElementRef; constructor() { afterNextRender(() => { // `scrollHeight` をチェックしても安全です。これはブラウザでのみ実行され、サーバーでは実行されないためです。 console.log('content height: ' + this.contentRef.nativeElement.scrollHeight); }); }}
Angular Service Worker の使用
サーバーでAngularをAngular Service Workerと組み合わせて使用している場合、動作は通常のサーバーサイドレンダリングの動作とは異なります。最初のサーバーリクエストは、期待どおりにサーバーでレンダリングされます。ただし、その最初のリクエストの後、後続のリクエストはService Workerによって処理され、常にクライアントサイドでレンダリングされます。