Dev Study
← 解説「@defer — 重い部分を後から読み込む」に戻る

サンプルコードで身につける: @defer — 重い部分を後から読み込む

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

1最小の @defer

@defer { ... } で囲んだ部分のコンポーネントは、最初のページ表示用のJavaScriptに含まれず、あとから別ファイルとして読み込まれます。条件を指定しない場合はページの描画が落ち着いたタイミング(on idle)で読み込みが始まります。初期表示を軽くする、というこの仕組みの核だけを示した形です。

import { Component } from '@angular/core';
import { HeavyChartComponent } from './heavy-chart.component';

@Component({
  selector: 'app-report-page',
  imports: [HeavyChartComponent],
  // @defer で囲んだ部分は初期表示のJSに含まれず、後から読み込まれる
  template:
    '<h1>売上レポート</h1>' +
    '@defer {' +
    '  <app-heavy-chart />' +
    '}',
})
export class ReportPageComponent {}

2@placeholder / @loading / @error をセットで書く

@placeholder は読み込み開始前、@loading は読み込み中、@error は失敗時の表示です。3つを用意しておくと、読み込みのどの段階でも画面に「何か」が表示され、ガタつきのない自然な見え方になります。実務で @defer を使うときは、少なくとも @placeholder を添えるのが基本です。

import { Component } from '@angular/core';
import { CommentListComponent } from './comment-list.component';

@Component({
  selector: 'app-article',
  imports: [CommentListComponent],
  // 読み込みの段階ごとに表示を用意しておく
  template:
    '@defer {' +
    '  <app-comment-list />' +
    '} @placeholder {' +
    '  <p>コメント欄はここに表示されます</p>' +
    '} @loading {' +
    '  <p>コメントを読み込み中...</p>' +
    '} @error {' +
    '  <p>読み込みに失敗しました</p>' +
    '}',
})
export class ArticleComponent {}

3on viewport — 画面に見えたら読み込む

@defer (on viewport) は、その部分がスクロールで画面内に入ったときに読み込みを始めます。ページ下部のグラフや地図など「最初は見えていない重い部品」にぴったりで、見られないまま終わる部品のコードを読み込まずに済みます。on viewport では @placeholder が必須で、その要素が画面に入ったかどうかで判定されます。

import { Component } from '@angular/core';
import { StoreMapComponent } from './store-map.component';

@Component({
  selector: 'app-shop-info',
  imports: [StoreMapComponent],
  // ページ下部の地図は、スクロールで見えたときに初めて読み込む
  template:
    '<h1>店舗情報</h1>' +
    '<p>(長い説明文がここに続く...)</p>' +
    '@defer (on viewport) {' +
    '  <app-store-map />' +
    '} @placeholder {' +
    '  <div class="map-area">地図はここに表示されます</div>' +
    '}',
})
export class ShopInfoComponent {}

4on interaction — ユーザーが触れたら読み込む

@defer (on interaction) は、@placeholder の要素をクリックしたりキー入力したりしたときに読み込みを始めます。動画プレイヤーや詳細フォームなど「使う人だけが使う部品」に向いており、興味のない利用者には一切のコードを届けずに済みます。触れるまで読み込まれない、という最も遅延の強いタイミング指定です。

import { Component } from '@angular/core';
import { VideoPlayerComponent } from './video-player.component';

@Component({
  selector: 'app-lesson-page',
  imports: [VideoPlayerComponent],
  // placeholder をクリックしたときに初めてプレイヤーを読み込む
  template:
    '@defer (on interaction) {' +
    '  <app-video-player />' +
    '} @placeholder {' +
    '  <button>クリックして動画を再生</button>' +
    '} @loading {' +
    '  <p>プレイヤーを準備中...</p>' +
    '}',
})
export class LessonPageComponent {}

5when — 自分の条件で読み込みを始める

@defer (when 条件) と書くと、条件が true になった瞬間に読み込みが始まります。「タブを切り替えたら」「ボタンを押したら」のように、アプリの状態を条件にして読み込みタイミングを自分で制御できます。シグナルの値を条件にすれば、状態の変化にそのまま反応します。

import { Component, signal } from '@angular/core';
import { SalesDetailComponent } from './sales-detail.component';

@Component({
  selector: 'app-dashboard',
  imports: [SalesDetailComponent],
  // showDetail() が true になった瞬間に読み込みが始まる
  template:
    '<button (click)="showDetail.set(true)">詳細を表示</button>' +
    '@defer (when showDetail()) {' +
    '  <app-sales-detail />' +
    '} @placeholder {' +
    '  <p>ボタンを押すと詳細データを表示します</p>' +
    '}',
})
export class DashboardComponent {
  showDetail = signal(false);
}