詳細ガイド
サーバーサイド・ハイブリッドレンダリング

サーバーサイドレンダリング

サーバーサイドレンダリング (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や機能は、サーバーでは使用できない場合があります。アプリケーションは、windowdocumentnavigatorlocation などのブラウザ固有のグローバルオブジェクト、および 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によって処理され、常にクライアントサイドでレンダリングされます。