Angular @defer 完全ガイド 2026:宣言的遅延読み込みと面接対策

Angular @deferブロックによる宣言的遅延読み込みの仕組み、トリガー種類、プリフェッチ戦略、インクリメンタルハイドレーション、面接質問を詳しく解説します。

Angular @defer 宣言的遅延読み込みガイド

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つのサブブロックをサポートしています:

app.component.htmltypescript
@defer (on viewport) {
  <analytics-dashboard />
} @placeholder {
  <div class="h-64 bg-muted animate-pulse"></div>
} @loading (minimum 200ms) {
  <loading-spinner />
} @error {
  <p>Failed to load the dashboard. Please refresh.</p>
}

@placeholder はトリガーが発火する前にレンダリングされます。@loading はチャンクのダウンロード中に表示され、フリッカーを防止するためのオプションの minimum 期間を設定できます。@error はネットワーク障害やチャンクエラーをキャッチします。@loadingminimum パラメータにより、チャンクがキャッシュから高速に読み込まれた際のスピナーの一瞬表示を防ぎます。

重要な制約として、@defer 内のすべての依存関係はスタンドアロンである必要があります。NgModule で宣言された非スタンドアロンコンポーネントは遅延化できず、@defer ラッパーに関係なく即座に読み込まれます。

トリガーの種類:コンポーネント読み込みタイミングの制御

Angularは7つの組み込みトリガーを提供しており、それぞれ異なる読み込み戦略に対応しています。複数のトリガーはセミコロンで結合でき、OR条件として評価されます。

product-page.component.htmltypescript
// ブラウザがアイドル状態になったら読み込み(デフォルト)
@defer {
  <recommendation-engine />
}

// 要素がビューポートに入ったら読み込み
@defer (on viewport) {
  <customer-reviews [productId]="product.id" />
}

// クリックまたはキーダウンで読み込み
@defer (on interaction) {
  <product-configurator />
} @placeholder {
  <button>Configure this product</button>
}

// マウスオーバーまたはフォーカスで読み込み
@defer (on hover) {
  <quick-preview />
}

// 3秒後に読み込み
@defer (on timer(3s)) {
  <chat-widget />
}

// 初期レンダリング直後に読み込み
@defer (on immediate) {
  <notification-center />
}

on viewport トリガーは内部的に Intersection Observer API を使用しています。on idle トリガーは requestIdleCallback に委任するため、ファーストビュー以下のコンテンツに対して最も安全なデフォルトです。on timer トリガーはミリ秒(500ms)または秒(3s)で期間を指定できます。

when トリガーによるリアクティブ条件

イベントベースのトリガーに加えて、when はシグナルの読み取りを含む任意のboolean式を受け付けます:

dashboard.component.tstypescript
export class DashboardComponent {
  showAdvanced = signal(false);
}

// dashboard.component.html
@defer (when showAdvanced()) {
  <advanced-analytics />
} @placeholder {
  <button (click)="showAdvanced.set(true)">Show advanced analytics</button>
}

onwhen の組み合わせは有効です。Angularはこれらを OR として扱い、いずれかの条件が満たされた時点でブロックが読み込まれます。

プリフェッチ:ダウンロードと表示の分離

プリフェッチは、チャンクのダウンロードタイミングとコンポーネントのレンダリングタイミングを分離します。ユーザーがトリガーを発火させる前にJavaScriptを取得することで、体感的なレイテンシを解消します。

settings.component.htmltypescript
@defer (on interaction; prefetch on idle) {
  <account-settings />
} @placeholder {
  <button>Open settings</button>
}

このパターンでは、ブラウザはアイドル時間中に account-settings チャンクをダウンロードします。ユーザーがクリックすると、コードがすでに利用可能であるため、コンポーネントは即座にレンダリングされます。プリフェッチは、メインの on 句と同じトリガータイプを受け付けます。

ダッシュボードでの一般的なパターンとして、重いコンポーネントをアイドル時にプリフェッチし、ビューポート進入時にレンダリングを遅延させる方法があります。

analytics.component.htmltypescript
@defer (on viewport; prefetch on idle) {
  <chart-widget [data]="salesData()" />
} @placeholder (minimum 100ms) {
  <div class="h-48 bg-muted rounded-none"></div>
}

@placeholderminimum により、ユーザーが高速スクロールして遅延コンポーネントがほぼ即座に読み込まれる場合のレイアウトフラッシュを防止します。

Angularの面接対策はできていますか?

インタラクティブなシミュレーター、flashcards、技術テストで練習しましょう。

@defer とサーバーサイドレンダリング

サーバー上では、@defer ブロックはデフォルトで @placeholder コンテンツをレンダリングします。遅延コンポーネントはサーバーサイドではレンダリングされません。この動作は意図的なもので、チャンクのダウンロードはブラウザコンテキストでのみ意味があるためです。

SEO上重要なコンテンツの場合、@placeholder にはスピナーだけでなく意味のあるHTMLを含める必要があります:

blog-post.component.htmltypescript
@defer (on viewport) {
  <comment-section [postId]="post.id" />
} @placeholder {
  <section aria-label="Comments">
    <h2>Comments</h2>
    <p>Loading comments...</p>
  </section>
}

検索エンジンはSSR時にプレースホルダーコンテンツをインデックスします。記述的なプレースホルダーにより、ページのセマンティック的な完全性が維持されます。

インクリメンタルハイドレーションと @defer

Angular 19 で開発者プレビューとして導入されたインクリメンタルハイドレーションは、Angular 22 で本番環境対応の機能として安定化されました。インクリメンタルハイドレーションは @deferhydrate トリガーで拡張し、サーバーレンダリングされたコンポーネントがクライアント上でいつインタラクティブになるかを制御します。

product-page.component.htmltypescript
@defer (hydrate on viewport) {
  <product-gallery [images]="product.images" />
}

@defer (hydrate on interaction) {
  <product-reviews [productId]="product.id" />
}

標準の @defer とは異なり、インクリメンタルハイドレーションはサーバー上で完全なコンポーネントHTMLをレンダリングします。クライアントは完全なマークアップを受け取りますが、トリガーが発火するまでコンポーネントのハイドレーションをスキップします。結果として、ブラウザは完全なページを即座に描画しますが、JavaScript実行は遅延されます。

Angularチームは、コンテンツの多いページでインクリメンタルハイドレーションを使用した場合、LCPスコアが一貫して40〜50%改善されると報告しています。Angular 22のAngular DevToolsは、コンポーネントインスペクターでハイドレーション状態(待機中、ハイドレーション済み、エラー)を直接可視化します。

実践的パターンとパフォーマンス戦略

パターン1:複数の遅延パネルを持つダッシュボード

dashboard.component.htmltypescript
<div class="grid grid-cols-2 gap-4">
  @defer (on viewport; prefetch on idle) {
    <sales-chart />
  } @placeholder {
    <skeleton-card height="300px" />
  }

  @defer (on viewport; prefetch on idle) {
    <user-activity-feed />
  } @placeholder {
    <skeleton-card height="300px" />
  }

  @defer (on interaction) {
    <export-panel />
  } @placeholder {
    <button>Export data</button>
  }
</div>

各パネルは独立して読み込まれます。エクスポートパネルはユーザーが明示的にリクエストするまで未読み込みのままで、エクスポートしないユーザーのチャンクダウンロードを節約します。

パターン2:シグナルによる条件付き機能読み込み

editor.component.tstypescript
export class EditorComponent {
  user = inject(UserService).currentUser;
  isPremium = computed(() => this.user()?.plan === 'premium');
}

// editor.component.html
@defer (when isPremium()) {
  <ai-assistant />
} @placeholder {
  <upgrade-banner />
}

無料プランのユーザーはAIアシスタントのチャンクをダウンロードすることがありません。アップグレードバナーはプレースホルダーとコンバージョン促進の両方の役割を果たします。

よくある落とし穴の回避

同じトリガーを持つ @defer ブロックをネストすると、同時にチャンクがダウンロードされてしまいます。ネストされたブロック間でトリガーを分散させることが推奨されます:

typescript
// 避けるべき例:両方がアイドル時に同時に発火
@defer {
  @defer {
    <nested-heavy-component />
  }
}

// 推奨:外部はアイドル、内部はビューポート
@defer (on idle) {
  <wrapper-component />
}
// wrapper-component テンプレート内:
@defer (on viewport) {
  <nested-heavy-component />
}

もう一つの落とし穴は、クリティカルレンダリングパスにすでに存在するコンポーネントに @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面接質問で頻出するパターンです。

Q: 非スタンドアロンコンポーネントを @defer 内に配置するとどうなりますか? コンポーネントは @defer ブロックに関係なく即座に読み込まれます。Angularコンパイラは NgModule で宣言されたコンポーネントを個別のチャンクに抽出できません。エラーは発生しませんが、最適化は暗黙的に失敗します。

Q: SSR時に @defer はどのように動作しますか? サーバーは @placeholder コンテンツをレンダリングします。遅延コンポーネントはサーバーサイドでレンダリングされません。ハイドレーションは、トリガーが発火した時点でクライアント上で行われます(インクリメンタルハイドレーションの場合は hydrate on を使用)。

Q: @defer は OnPush 変更検知と併用できますか? はい。@defer はテンプレートレベルの機能であり、どの変更検知戦略とも併用できます。遅延コンポーネントは読み込まれた後、独自の変更検知設定に従います。

Q: on idleon immediate の違いは何ですか? on idlerequestIdleCallback を通じてブラウザがすべての保留中の処理を完了するのを待ちます。on immediate はAngularが初期レンダリングパスを完了した直後に、アイドル状態を待たずにトリガーされます。on immediate は、ファーストペイントをブロックすべきではないが早期に読み込むべきコンポーネントに適しています。

Angular面接対策の幅広い内容については、Angular変更検知モジュールAngularシグナルモジュールを参照してください。

今すぐ練習を始めましょう!

面接シミュレーターと技術テストで知識をテストしましょう。

まとめ

  • @defer はスタンドアロンコンポーネントを個別のチャンクに分割し、ルートの再構成なしに初期バンドルサイズを削減します
  • 7つのトリガータイプ(idleviewportinteractionhovertimerimmediatewhen)が、パッシブからユーザー駆動まであらゆる読み込み戦略をカバーします
  • prefetch on はチャンクのダウンロードとコンポーネントのレンダリングを分離し、ユーザートリガーコンポーネントの体感レイテンシを解消します
  • インクリメンタルハイドレーション(hydrate on)はサーバー上で完全なHTMLをレンダリングしつつJavaScript実行をクライアントに遅延させ、コンテンツの多いページで40〜50%のLCP改善を実現します
  • @placeholder コンテンツはSSR時にレンダリングされるため、SEOのためにセマンティック的に意味のある内容にする必要があります
  • @defer はルーターベースの遅延読み込みを補完します:ルートは機能レベルの分割に、@defer はテンプレート内のコンポーネントレベルの分割に使用します
  • 非スタンドアロンコンポーネントは遅延化できず、暗黙的に即座読み込みされます

今すぐ練習を始めましょう!

面接シミュレーターと技術テストで知識をテストしましょう。

タグ

#angular
#angular-defer
#lazy-loading
#performance
#ssr
#signals
#deep-dive

共有

関連記事