Dev Study
← 解説「ハイドレーション — サーバのHTMLをそのまま引き継ぐ」に戻る

サンプルコードで身につける: ハイドレーション — サーバのHTMLをそのまま引き継ぐ

解説で学んだ概念を、5つの具体例で確認します。素朴な例から実務寄りの例へと進みます。

1ハイドレーション有り・無しの違い

設定1つで、届いたHTMLを「作り直す」か「再利用する」かが変わります。無しでも一応動きますが、ちらつきと無駄が発生します。動きをタイムラインで比べます。

// ── provideClientHydration() なし ──
// 1. サーバ製HTMLが表示される
// 2. Angularが起動
// 3. 既存のDOMを破棄して、同じ画面をゼロから再構築 ← 無駄
//    (一瞬ちらつく・スクロール位置が飛ぶことも)
//
// ── provideClientHydration() あり ──
// 1. サーバ製HTMLが表示される
// 2. Angularが起動
// 3. 既存のDOMと自分の描画結果を照合し、
//    DOMはそのまま、イベントと状態だけを紐付ける ← 速い

2設定は app.config.ts に1行

ハイドレーションの有効化はprovidersに1行追加するだけです。ng add @angular/ssrを使った場合は自動で入っています。自分で設定を確認するときはここを見ます。

import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';
import { provideClientHydration } from '@angular/platform-browser';
import { routes } from './app.routes';

export const appConfig: ApplicationConfig = {
  providers: [
    provideRouter(routes),
    provideClientHydration(), // ← これだけ
  ],
};

// 起動時にコンソールへハイドレーションの統計
// (再利用できたノード数など)が表示される

3照合が失敗するHTMLの例

ハイドレーションはサーバとブラウザのDOMが一致する前提で動きます。HTML仕様上不正な入れ子はブラウザが勝手に構造を直してしまうため、照合エラー(NG0500)の典型的な原因になります。

<!-- × 照合が失敗しやすい例 -->

<!-- table の直下に tr 以外を置く(ブラウザが並べ替える) -->
<table>
  <div>合計: 300円</div>
  <tr><td>りんご</td></tr>
</table>

<!-- p の中にブロック要素(ブラウザが p を分割する) -->
<p>説明文 <div>注意書き</div></p>

<!-- ○ 正しい入れ子に直せば照合は通る -->
<!-- エラー NG0500 が出たら、まずHTMLの入れ子を疑う -->

4ngSkipHydration で部分的に除外する

Angularの外でDOMを書き換えるライブラリを使う部品は、どうしても照合が合いません。そのコンポーネントの要素にngSkipHydrationを付ければ、そこだけ従来通り作り直しになります。あくまで最終手段です。

// 地図ライブラリなどが内部でDOMを直接書き換える部品は、
// サーバ製HTMLと一致しなくなるので、そこだけ除外する

@Component({
  selector: 'app-dashboard',
  template:
    '<h1>ダッシュボード</h1>' + // ここは普通にハイドレート
    '<app-legacy-map ngSkipHydration />', // ここだけ作り直し
})
export class DashboardComponent {}

// 注意: 付けた部品はハイドレーションの恩恵(ちらつき防止)を
// 失う。広い範囲に付けるくらいなら原因のDOM不一致を直す

5withEventReplay — 紐付け前のクリックを取りこぼさない

HTMLが見えてからハイドレーション完了までの間にユーザーがボタンを押すと、本来は何も起きません。withEventReplayを足すと、その間の操作を記録しておき、紐付け完了後に再生してくれます。

import {
  provideClientHydration,
  withEventReplay,
} from '@angular/platform-browser';

export const appConfig = {
  providers: [provideClientHydration(withEventReplay())],
};

// ── 効果 ──
// 1. HTMLが見える(まだJSは準備中)
// 2. ユーザーが「カートに入れる」をクリック ← 記録される
// 3. ハイドレーション完了
// 4. 記録されたクリックが自動で再生される ← 無反応にならない