← 解説「コンポーネントを追加すると何が変わるか — ビルド時の変化」に戻る
サンプルコードで身につける: コンポーネントを追加すると何が変わるか — ビルド時の変化
解説で学んだ概念を、5つの具体例で確認します。素朴な例から実務寄りの例へと進みます。
1追加前後で ɵcmp がどう変わるか
子コンポーネントをimportsに追加する前後で、親のコンパイル結果に起きる差分を概念コードで比べます。「参照が1本増える」ことがすべての起点です。
// ── 追加前: PageComponent.ɵcmp(概念)──
// dependencies: []
// template: 作成モードで <main> と <h1> を作る命令のみ
// ── UserCardComponent を imports してテンプレートで使うと ──
// dependencies: [UserCardComponent] ← クラス参照が増えた
// template: 作成モードに
// 「app-user-card のホスト要素を作る」
// 「UserCardComponent をこの位置で起動する」
// 命令が追加される
//
// この dependencies の参照こそが、
// ・バンドラーが「同梱すべき」と判断する根拠であり
// ・実行時に子の設計図(ɵcmp)へ到達する道になる
2NG8001 — 照合失敗エラーの正体
importsに書き忘れたときのお馴染みのエラーを、内部機構の言葉で読み直します。「実行時に部品が見つからない」のではなく「ビルド時の突き合わせで相手がいなかった」というエラーです。
// NG8001: 'app-user-card' is not a known element
//
// コンパイラの思考の流れ:
// 1. <app-user-card> というタグを見つけた
// 2. 標準のHTML要素リストに無い
// 3. この component の imports の selector とも一致しない
// 4. → 照合先が無いので報告する
//
// つまり直し方は「照合候補に載せる」の一択:
@Component({
imports: [UserCardComponent], // ← これで照合が通る
template: '<app-user-card />',
})
export class PageComponent {}
//
// 逆パターンの NG8113(imports に書いたが未使用)は
// 「照合候補に載せたのに一度もマッチしなかった」という警告
3セレクタ照合はビルド時 — だからタグ名を動的にできない
「タグ名を変数にして出し分けたい」が素直に書けないのは、照合がビルド時に終わっているからです。動的にコンポーネントを選びたいときの正攻法(ViewContainerRef.createComponent)も合わせて押さえます。
// × こういうことはできない(照合はビルド時に済んでいるため)
// template: '<{{ tagName() }} />'
// ○ 実行時にコンポーネントを選びたいときの正攻法:
// クラス参照を渡して「ここに生やして」と依頼する
import { Component, viewChild, ViewContainerRef, inject } from '@angular/core';
import { LineChartComponent } from './line-chart.component';
import { BarChartComponent } from './bar-chart.component';
@Component({
selector: 'app-report',
template: '<ng-container #outlet />',
})
export class ReportComponent {
private outlet = viewChild.required('outlet', {
read: ViewContainerRef,
});
show(type: 'line' | 'bar') {
const cmp = type === 'line' ? LineChartComponent : BarChartComponent;
this.outlet().clear();
this.outlet().createComponent(cmp); // 誕生手順を明示的に起動
}
}
4参照グラフとチャンク分割の関係
同じ部品でも「どこから参照されるか」で入るチャンクが変わります。ビルド出力のファイル一覧と参照の関係を対応づけて読むと、初期表示に響く追加かどうかを判断できます。
ng build
# Initial chunk files:
# main-XXXX.js 512.3 kB ← 起動時に必ず読む
# Lazy chunk files:
# chunk-ADMIN.js 84.1 kB ← 遅延ルートで初めて読む
# UserCardComponent をどこに追加したかで行き先が決まる:
#
# AppComponent(初期表示)から参照
# → main に入る。初期バンドルが太る
#
# 遅延ルートの AdminPage からだけ参照
# → chunk-ADMIN に入る。初期表示への影響ゼロ
#
# 両方から参照
# → 共有チャンクに切り出されることもある
#
# 「追加=参照グラフに節点を足す」なので、
# 参照元がチャンクの行き先を決める
5追加の全行程を1枚にまとめる
ng generateからブラウザ表示までの全行程を俯瞰します。次のレッスン以降で学ぶ実行時の話がどこに接続するのかも、この地図で位置づけられます。
// ── コンポーネント追加の全行程 ──
//
// [開発] ng generate component user-card
// ↓ imports に追加、テンプレートで使用
//
// [ビルド] セレクタ照合(NG8001はここで出る)
// ↓ ɵcmp 生成 + 親の依存リストに参照追加
// ↓ 参照グラフに従ってチャンクへ配置
//
// [配信] ただのJSファイルとしてブラウザへ
// ↓
// [実行時] 親の作成モードが子のホスト要素を作る
// ↓ 誕生手順(DI→input→フック→再帰)…次レッスン
// ↓ 変更検知が更新モードを走らせる …後のレッスン
// 破棄の逆再生 …最後のレッスン