# Angular @defer 完全ガイド 2026:宣言的遅延読み込みと面接対策
> Angular @deferブロックによる宣言的遅延読み込みの仕組み、トリガー種類、プリフェッチ戦略、インクリメンタルハイドレーション、面接質問を詳しく解説します。
- Published: 2026-06-14
- Updated: 2026-06-14
- Author: SharpSkill
- Tags: angular, angular-defer, lazy-loading, performance, ssr, signals, deep-dive
- Reading time: 9 min
---
Angular の `@defer` ブロックは、ルーターベースの遅延読み込みでは解決できなかった課題を解決します。アプリケーションを別々のルートに再構成することなく、個々のコンポーネント、ディレクティブ、パイプをオンデマンドで読み込むことが可能になりました。Angular 17 で導入され、Angular 22 で安定化された `@defer` は、動的インポート、`*ngIf` フラグ、webpack設定を組み合わせて実現していた処理を、単一の宣言的テンプレートブロックに変換します。
> **@defer の役割**
>
> `@defer` は、ラップされたコンポーネントを個別のJavaScriptチャンクに分割し、トリガー条件が発火した場合にのみ読み込むようAngularコンパイラに指示します。ブラウザが初期段階でダウンロードするJavaScriptが減少し、手動のコード分割設定なしでLargest Contentful Paint(LCP)とTime to First Byte(TTFB)が改善されます。
## @defer の内部動作メカニズム
Angularコンパイラが `@defer` ブロックを検出すると、その内部のすべてのスタンドアロンコンポーネント、ディレクティブ、パイプを個別のチャンクに抽出します。メインバンドルはこれらの依存関係なしで配信されます。実行時に、Angularはトリガー条件を評価し、動的 `import()` 呼び出しでチャンクを取得します。
このブロックは、読み込みライフサイクル中にユーザーに表示される内容を制御する4つのサブブロックをサポートしています:
```typescript
// app.component.html
@defer (on viewport) {
} @placeholder {
} @loading (minimum 200ms) {
} @error {
Failed to load the dashboard. Please refresh.
}
```
`@placeholder` はトリガーが発火する前にレンダリングされます。`@loading` はチャンクのダウンロード中に表示され、フリッカーを防止するためのオプションの `minimum` 期間を設定できます。`@error` はネットワーク障害やチャンクエラーをキャッチします。`@loading` の `minimum` パラメータにより、チャンクがキャッシュから高速に読み込まれた際のスピナーの一瞬表示を防ぎます。
重要な制約として、`@defer` 内のすべての依存関係はスタンドアロンである必要があります。`NgModule` で宣言された非スタンドアロンコンポーネントは遅延化できず、`@defer` ラッパーに関係なく即座に読み込まれます。
## トリガーの種類:コンポーネント読み込みタイミングの制御
Angularは7つの組み込みトリガーを提供しており、それぞれ異なる読み込み戦略に対応しています。複数のトリガーはセミコロンで結合でき、OR条件として評価されます。
```typescript
// product-page.component.html
// ブラウザがアイドル状態になったら読み込み(デフォルト)
@defer {
}
// 要素がビューポートに入ったら読み込み
@defer (on viewport) {
}
// クリックまたはキーダウンで読み込み
@defer (on interaction) {
} @placeholder {
}
// マウスオーバーまたはフォーカスで読み込み
@defer (on hover) {
}
// 3秒後に読み込み
@defer (on timer(3s)) {
}
// 初期レンダリング直後に読み込み
@defer (on immediate) {
}
```
`on viewport` トリガーは内部的に [Intersection Observer API](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API) を使用しています。`on idle` トリガーは `requestIdleCallback` に委任するため、ファーストビュー以下のコンテンツに対して最も安全なデフォルトです。`on timer` トリガーはミリ秒(`500ms`)または秒(`3s`)で期間を指定できます。
### `when` トリガーによるリアクティブ条件
イベントベースのトリガーに加えて、`when` はシグナルの読み取りを含む任意のboolean式を受け付けます:
```typescript
// dashboard.component.ts
export class DashboardComponent {
showAdvanced = signal(false);
}
// dashboard.component.html
@defer (when showAdvanced()) {
} @placeholder {
}
```
`on` と `when` の組み合わせは有効です。Angularはこれらを OR として扱い、いずれかの条件が満たされた時点でブロックが読み込まれます。
## プリフェッチ:ダウンロードと表示の分離
プリフェッチは、チャンクのダウンロードタイミングとコンポーネントのレンダリングタイミングを分離します。ユーザーがトリガーを発火させる前にJavaScriptを取得することで、体感的なレイテンシを解消します。
```typescript
// settings.component.html
@defer (on interaction; prefetch on idle) {
} @placeholder {
}
```
このパターンでは、ブラウザはアイドル時間中に `account-settings` チャンクをダウンロードします。ユーザーがクリックすると、コードがすでに利用可能であるため、コンポーネントは即座にレンダリングされます。プリフェッチは、メインの `on` 句と同じトリガータイプを受け付けます。
ダッシュボードでの一般的なパターンとして、重いコンポーネントをアイドル時にプリフェッチし、ビューポート進入時にレンダリングを遅延させる方法があります。
```typescript
// analytics.component.html
@defer (on viewport; prefetch on idle) {
} @placeholder (minimum 100ms) {
}
```
`@placeholder` の `minimum` により、ユーザーが高速スクロールして遅延コンポーネントがほぼ即座に読み込まれる場合のレイアウトフラッシュを防止します。
## @defer とサーバーサイドレンダリング
サーバー上では、`@defer` ブロックはデフォルトで `@placeholder` コンテンツをレンダリングします。遅延コンポーネントはサーバーサイドではレンダリングされません。この動作は意図的なもので、チャンクのダウンロードはブラウザコンテキストでのみ意味があるためです。
SEO上重要なコンテンツの場合、`@placeholder` にはスピナーだけでなく意味のあるHTMLを含める必要があります:
```typescript
// blog-post.component.html
@defer (on viewport) {
} @placeholder {
Comments
Loading comments...
}
```
検索エンジンはSSR時にプレースホルダーコンテンツをインデックスします。記述的なプレースホルダーにより、ページのセマンティック的な完全性が維持されます。
## インクリメンタルハイドレーションと @defer
Angular 19 で開発者プレビューとして導入された[インクリメンタルハイドレーション](https://angular.dev/guide/incremental-hydration)は、Angular 22 で本番環境対応の機能として安定化されました。インクリメンタルハイドレーションは `@defer` を `hydrate` トリガーで拡張し、サーバーレンダリングされたコンポーネントがクライアント上でいつインタラクティブになるかを制御します。
```typescript
// product-page.component.html
@defer (hydrate on viewport) {
}
@defer (hydrate on interaction) {
}
```
標準の `@defer` とは異なり、インクリメンタルハイドレーションはサーバー上で完全なコンポーネントHTMLをレンダリングします。クライアントは完全なマークアップを受け取りますが、トリガーが発火するまでコンポーネントのハイドレーションをスキップします。結果として、ブラウザは完全なページを即座に描画しますが、JavaScript実行は遅延されます。
Angularチームは、コンテンツの多いページでインクリメンタルハイドレーションを使用した場合、LCPスコアが一貫して40〜50%改善されると報告しています。Angular 22のAngular DevToolsは、コンポーネントインスペクターでハイドレーション状態(待機中、ハイドレーション済み、エラー)を直接可視化します。
## 実践的パターンとパフォーマンス戦略
### パターン1:複数の遅延パネルを持つダッシュボード
```typescript
// dashboard.component.html
@defer (on viewport; prefetch on idle) {
} @placeholder {
}
@defer (on viewport; prefetch on idle) {
} @placeholder {
}
@defer (on interaction) {
} @placeholder {
}
```
各パネルは独立して読み込まれます。エクスポートパネルはユーザーが明示的にリクエストするまで未読み込みのままで、エクスポートしないユーザーのチャンクダウンロードを節約します。
### パターン2:シグナルによる条件付き機能読み込み
```typescript
// editor.component.ts
export class EditorComponent {
user = inject(UserService).currentUser;
isPremium = computed(() => this.user()?.plan === 'premium');
}
// editor.component.html
@defer (when isPremium()) {
} @placeholder {
}
```
無料プランのユーザーはAIアシスタントのチャンクをダウンロードすることがありません。アップグレードバナーはプレースホルダーとコンバージョン促進の両方の役割を果たします。
### よくある落とし穴の回避
同じトリガーを持つ `@defer` ブロックをネストすると、同時にチャンクがダウンロードされてしまいます。ネストされたブロック間でトリガーを分散させることが推奨されます:
```typescript
// 避けるべき例:両方がアイドル時に同時に発火
@defer {
@defer {
}
}
// 推奨:外部はアイドル、内部はビューポート
@defer (on idle) {
}
// wrapper-component テンプレート内:
@defer (on viewport) {
}
```
もう一つの落とし穴は、クリティカルレンダリングパスにすでに存在するコンポーネントに `@defer` を使用することです。ファーストビューのコンテンツを遅延化すると、ブラウザが描画前にチャンクのダウンロードと実行を行う必要があるため、LCPが増加します。`@defer` はファーストビュー以下またはユーザートリガーのコンテンツに限定して使用してください。
## @defer vs ルーターベースの遅延読み込み
ルーターベースの遅延読み込みと `@defer` は互いに補完し合います。ルーターの遅延読み込みはルートレベルで分割し、機能モジュール全体を遅延させます。`@defer` はテンプレート内で分割し、個々のコンポーネントを遅延させます。
| 観点 | ルーター遅延読み込み | @defer |
|------|---------------------|--------|
| 粒度 | ルートレベル | コンポーネントレベル |
| トリガー | ナビゲーションイベント | viewport, idle, interaction, hover, timer, 条件 |
| SSR動作 | 完全なサーバーレンダリング | サーバー上でプレースホルダー |
| スタンドアロン必須 | いいえ(NgModulesで動作) | はい(スタンドアロンのみ) |
| プリフェッチ | `preloadingStrategy` | `prefetch on` 句 |
| ユースケース | 機能モジュール、ページ | ウィジェット、パネル、条件付きUI |
典型的なアプリケーションでは両方を使用します:トップレベルの機能エリアにはルーター遅延読み込みを、それらのエリア内の重いコンポーネントには `@defer` を適用します。
## Angular @defer の面接質問
技術面接では、`@defer` がAngularのパフォーマンス戦略の中核となるにつれ、この機能に関する質問が増加しています。以下は[Angular面接質問](/technologies/angular/interview-questions/standalone-components)で頻出するパターンです。
**Q: 非スタンドアロンコンポーネントを @defer 内に配置するとどうなりますか?**
コンポーネントは `@defer` ブロックに関係なく即座に読み込まれます。Angularコンパイラは NgModule で宣言されたコンポーネントを個別のチャンクに抽出できません。エラーは発生しませんが、最適化は暗黙的に失敗します。
**Q: SSR時に @defer はどのように動作しますか?**
サーバーは `@placeholder` コンテンツをレンダリングします。遅延コンポーネントはサーバーサイドでレンダリングされません。ハイドレーションは、トリガーが発火した時点でクライアント上で行われます(インクリメンタルハイドレーションの場合は `hydrate on` を使用)。
**Q: @defer は OnPush 変更検知と併用できますか?**
はい。`@defer` はテンプレートレベルの機能であり、どの変更検知戦略とも併用できます。遅延コンポーネントは読み込まれた後、独自の変更検知設定に従います。
**Q: `on idle` と `on immediate` の違いは何ですか?**
`on idle` は `requestIdleCallback` を通じてブラウザがすべての保留中の処理を完了するのを待ちます。`on immediate` はAngularが初期レンダリングパスを完了した直後に、アイドル状態を待たずにトリガーされます。`on immediate` は、ファーストペイントをブロックすべきではないが早期に読み込むべきコンポーネントに適しています。
Angular面接対策の幅広い内容については、[Angular変更検知モジュール](/technologies/angular/interview-questions/change-detection)や[Angularシグナルモジュール](/technologies/angular/interview-questions/angular-signals)を参照してください。
## まとめ
- `@defer` はスタンドアロンコンポーネントを個別のチャンクに分割し、ルートの再構成なしに初期バンドルサイズを削減します
- 7つのトリガータイプ(`idle`、`viewport`、`interaction`、`hover`、`timer`、`immediate`、`when`)が、パッシブからユーザー駆動まであらゆる読み込み戦略をカバーします
- `prefetch on` はチャンクのダウンロードとコンポーネントのレンダリングを分離し、ユーザートリガーコンポーネントの体感レイテンシを解消します
- インクリメンタルハイドレーション(`hydrate on`)はサーバー上で完全なHTMLをレンダリングしつつJavaScript実行をクライアントに遅延させ、コンテンツの多いページで40〜50%のLCP改善を実現します
- `@placeholder` コンテンツはSSR時にレンダリングされるため、SEOのためにセマンティック的に意味のある内容にする必要があります
- `@defer` はルーターベースの遅延読み込みを補完します:ルートは機能レベルの分割に、`@defer` はテンプレート内のコンポーネントレベルの分割に使用します
- 非スタンドアロンコンポーネントは遅延化できず、暗黙的に即座読み込みされます
---
Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack.
HTML version of this page: https://sharpskill.dev/ja/blog/angular/angular-defer-loading-declarative-lazy-loading-guide