← 解説「TView と LView — 設計図と台帳」に戻る
サンプルコードで身につける: TView と LView — 設計図と台帳
解説で学んだ概念を、5つの具体例で確認します。素朴な例から実務寄りの例へと進みます。
1100行のリストでもTViewは1つ
行コンポーネントを大量に並べたときのメモリの持ち方を図にします。設計図の共有こそが「コンポーネントを気軽に量産してよい」根拠です。
// @for (item of items(); track item.id) { <app-row [item]="item" /> }
// items が100件のとき:
//
// RowComponent の TView(設計図) … 1つだけ
// 「スロット0: div、スロット1: テキスト、…」
//
// LView(台帳) … 100個
// 行1: [<div>参照, 'りんご', ...]
// 行2: [<div>参照, 'みかん', ...]
// ...
//
// 増えるのは台帳(値の分)だけで、構造情報は共有。
// 「部品を切る基準」のレッスンで細かく分けてよいと言えたのは、
// 分割のコストがこの台帳1枚分と小さいから
2更新モードの突き合わせを疑似コードで見る
「前回値と比べて、違うときだけDOMに触る」を疑似コードにします。LViewが前回値のキャッシュを兼ねていることが、Angularの更新が速い理由の核心です。
// ɵɵtextInterpolate(ctx.title()) が内部でやっていること(概念)
function textInterpolate(lView: any[], slot: number, newValue: unknown) {
const oldValue = lView[slot]; // 台帳から前回値を取り出す
if (oldValue === newValue) {
return; // 変化なし → DOMに一切触らない(ここが高速の理由)
}
lView[slot] = newValue; // 台帳を更新して
const textNode = lView[slot - 1]; // 対応するDOMノードの
textNode.textContent = String(newValue); // 中身だけ差し替え
}
// 比較は === なので、オブジェクトを同じ参照のまま中身だけ
// 書き換えると「変化なし」と判定される。イミュータブルに
// 作り直す習慣(スプレッド構文)はこの比較と相性がいい
3ファーストパス — 最初の1個目だけ仕事が多い
同じコンポーネントの1個目と2個目以降では、生成時の仕事量が違います。設計図は初回に一度だけ作られ、以降は使い回されます。計測で最初の1回だけ遅く見える現象の説明にもなります。
// RowComponent を初めて表示するとき(1個目):
// 1. TView が無い → テンプレート関数を「設計図作成モード」で
// 実行し、構造情報(TView)を組み立てる ← 1回きりの仕事
// 2. LView を作って初回描画
//
// 2個目以降:
// 1. TView は出来上がっている → 使い回し
// 2. LView を作って初回描画のみ
//
// 実務での現れ方:
// ・一覧の最初の1行だけ描画がわずかに重い
// ・性能計測で「初回だけ遅い」ときは、このウォームアップ分を
// 除いて2回目以降を測る(計測のレッスンの注意点の内部理由)
4「なぜAngularにVirtual DOMが無いのか」に答える
他フレームワーク経験者から必ず聞かれる質問に、TView/LViewの言葉で答えられるようにします。比較対象の持ち方が根本的に違うため、そもそも仮想ツリーの再構築が不要です。
// ── Virtual DOM 方式(React系)──
// 状態変更 → 仮想ツリーを丸ごと再生成
// → 前回の仮想ツリーと比較(diff)
// → 差分だけ実DOMへ反映
// 比較対象: 「ツリー構造」同士
//
// ── Angular(Ivy)方式 ──
// 状態変更 → 該当ビューの更新モードを実行
// → 台帳のスロットごとに前回値と比較
// → 違うスロットのDOMノードだけ書き換え
// 比較対象: 「バインディングの値」同士
//
// コンパイル時に「どこが変わりうるか」(バインディング位置)を
// 特定済みなので、実行時にツリーを作って探す必要がない。
// AOTコンパイルがあるからこそ成立する設計
5Angular DevToolsで台帳ツリーを覗く
デバッグ編で使ったAngular DevToolsを、内部機構の目で使い直します。表示されているツリーやプロパティが、内部のどのデータに対応するかが分かると読み解きが速くなります。
# Angular DevTools(Chrome拡張)の Components タブで:
#
# ・コンポーネントツリー
# → LView の親子ツリーの可視化。@for で並んだ行は
# 「同じ設計図から作られた台帳の並び」として見える
#
# ・ノードを選択して右に出るプロパティ
# → その LView が持つコンポーネントインスタンスの中身
#
# ・Profiler タブの記録
# → どのビューで更新モードが走ったかの記録。
# 変更検知のレッスン(次)を学ぶと読み方が変わる
#
# console で確認する小技:
# 要素を選択して ng.getComponent($0)
# → そのDOMノードに紐付くインスタンスを取得できる