Dev Study
← Angular コース

78. 変更検知 — 「いつ・どこを」再描画するかの世代交代

台帳(LView)の更新モードを「いつ・どのコンポーネントで走らせるか」を決める仕組みが変更検知です。zone.js時代の答えは「いつ=何か非同期処理が終わるたび、どこを=全部」でした。zone.jsは起動時にクリック・タイマー・HTTP応答などの非同期APIを横取り(パッチ)し、「何かが終わった、画面が変わったかもしれない」というたびに、ルートから全コンポーネントの更新モードを実行します(tick)。何が変わったか分からない前提なので全査定するしかなく、確実ですが画面が大きいほど無駄が増えます。

OnPush はこの全査定への剪定の印です。OnPush が付いた枝は「input が変わった・自分の中でイベントが起きた・使っているシグナルが変わった」場合にだけ査定され、それ以外は枝ごとスキップされます。v22 で OnPush が既定になったのは、この節約を標準の動きにしたということです。

そしてゾーンレス+シグナルが最終形です。テンプレートで title() と読んだ瞬間、「このビューは title に依存している」という記録が残ります。set() されると依存しているビューだけに「汚れた(dirty)」印が付き、スケジューラがそのビューの更新モードだけを走らせます。「何が変わったか分からないから全部見る」(zone.js)から「変わった場所が最初から分かっているからそこだけ見る」(シグナル)へ — この転換こそが、コースを通じて「状態はシグナルで」と繰り返してきたことの内部的な意味であり、zone.js を外せる(ゾーンレスにできる)理由です。

サンプルコード(フレームワーク環境が必要なため表示のみ)

// ── zone.js 時代: 全査定 ──
// クリック発生
//   → zone.js が検知して tick() を要求
//   → App → Header → Sidebar → Main → 全部の更新モードを実行
//     (それぞれの台帳で前回値と比較。ほとんどは「変化なし」)

// ── シグナル + ゾーンレス: 標的更新 ──
// count = signal(0);
//
// テンプレートで {{ count() }} と読む
//   → 「このビューは count に依存」と記録される(読む=登録)
//
// count.set(1);
//   → 依存している CounterComponent のビューだけ dirty になる
//   → スケジューラがそのビューの更新モードだけ実行
//   → Header や Sidebar には最初から行かない
サンプルコードで身につける(5本) →

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

公式ドキュメントで詳しく ↗