Async Components - 必要時にコンポーネントを遅延ロードする

ロードマップ: Vue.js学習ロードマップ

What(何についてか)

Async Components は、コンポーネントを初回ロード時にまとめて読み込まず、必要になった時にだけ遅延ロードする仕組みである。

Vue では defineAsyncComponent() を使って、Promise ベースでコンポーネントを読み込む wrapper component を定義できる。

Why(なぜ必要か)

大きなアプリケーションでは、すべてのコンポーネントを初回表示時にまとめてバンドルへ含めると、初期ロードが重くなりやすい。

特に以下のような部品は、初回表示に不要であることが多い。

  • 管理者ページ
  • モーダルの中身
  • 重いグラフやリッチエディタ
  • 非アクティブなタブの内容
  • 条件付きでしか表示されない設定画面

このような部品を必要時まで遅延ロードすることで、初期表示コストを下げられる。

How(どう動くか)

1. Basic Usage

defineAsyncComponent() に loader function を渡す。

import { defineAsyncComponent } from 'vue'
 
const AsyncComp = defineAsyncComponent(() =>
  import('./components/MyComponent.vue')
)

実務では、手書きの Promise よりも ES modules の dynamic import を使うのが一般的である。

この AsyncComp は wrapper component であり、実際に render されるタイミングで初めて loader が実行される。

flowchart LR
  A["Parent renders AsyncComp"] --> B["loader runs"]
  B --> C["chunk loaded"]
  C --> D["inner component rendered"]

2. props / slots は透過的に渡る

Async component wrapper は、受け取った props と slots を内部コンポーネントへそのまま渡す。 したがって、利用側は通常のコンポーネントとほぼ同じ API で扱える。

3. global / local / script setup どこでも定義可能

  • app.component() による global registration
  • components option による local registration
  • <script setup> 内での直接定義

いずれでも使える。

Loading and Error States

非同期処理には loading と error が伴うため、defineAsyncComponent() では詳細オプションを指定できる。

const AsyncComp = defineAsyncComponent({
  loader: () => import('./Foo.vue'),
  loadingComponent: LoadingComponent,
  delay: 200,
  errorComponent: ErrorComponent,
  timeout: 3000
})

loadingComponent

読み込み中に表示する placeholder component。 spinner や skeleton の表示に使う。

delay

loadingComponent を表示するまでの待機時間。デフォルトは 200ms。 短時間のロードで loading UI が一瞬だけ点滅する flicker を防ぐためにある。

errorComponent

loader が reject した時に表示する component。 chunk load failure やネットワーク失敗時の UX を受け持つ。

timeout

一定時間を超えた場合に errorComponent を表示させるための閾値。デフォルトは Infinity。

実務での採用判断

Async Components は「常に使うべき標準設計」ではなく、必要な箇所へ適用するパフォーマンス最適化である。

async に向くケース

  • 初回表示に不要
  • 部品が重い
  • アクセス頻度が低い
  • 遅れて出ても UX 的に自然

async に向かないケース

  • ファーストビューで必ず必要
  • 軽量な共通部品
  • 直後にほぼ確実に使われる主要 UI

実務ルール

  • ページの主役は通常 import を優先
  • 後から出る重い脇役は async 候補
  • まず素直に作り、ボトルネックが見えたら計測して最適化する

Lazy Hydration

この節は SSR 利用時のみ意味を持つ。

SSR では、サーバーが返した HTML をクライアント側で Vue component として有効化する hydration が必要になる。 Lazy Hydration は、その hydration を即時ではなく条件付きで遅延させる仕組みである。

built-in strategies

  • hydrateOnIdle() : ブラウザが暇な時に hydrate
  • hydrateOnVisible() : 表示領域に入った時に hydrate
  • hydrateOnMediaQuery() : media query 条件成立時に hydrate
  • hydrateOnInteraction() : interaction 発生時に hydrate
import { defineAsyncComponent, hydrateOnVisible } from 'vue'
 
const AsyncComp = defineAsyncComponent({
  loader: () => import('./Comp.vue'),
  hydrate: hydrateOnVisible()
})

Async Components が「コードをいつ読むか」の制御であるのに対し、Lazy Hydration は「SSR 後の UI をいつ起動するか」の制御である。

Using with Suspense

Async Components は <Suspense> と併用できる。

  • 個別 component 単位の loading / error 制御は Async Components
  • 複数の非同期依存をまとめて待つ制御は Suspense

という役割分担で理解するとよい。

パフォーマンス最適化との向き合い方

Async Components は有効な最適化手段だが、早すぎる最適化は複雑さを増やす。

現実的な進め方は以下である。

  1. まず素直に実装する
  2. 重そうな部品だけ lazy load 候補として意識する
  3. 実測してボトルネックを確認する
  4. 問題が確認できた箇所だけ最適化する

Options API → Composition API 差分(補足)

項目Options API(旧)Composition API(新)
async component 定義場所components option 内に書くことが多い<script setup> で局所的に定義しやすい
ロジックの見通しregistration と周辺設定が option に分散しやすいimport と定義が近く、 lazy load 意図が見えやすい
SSR との連携利用可能だが抽象度が高いVue 3.5+ の hydration strategy と組み合わせやすい

Key Concepts

用語説明
defineAsyncComponent非同期コンポーネント wrapper を定義する API
dynamic importPromise を返す ES modules の遅延 import
loadingComponent読み込み中に表示する component
errorComponent読み込み失敗時に表示する component
delayloading UI の flicker を避けるための待機時間
timeout長時間待機時に失敗扱いへ切り替える閾値
lazy hydrationSSR 後の hydrate 実行タイミングを遅延する最適化
Suspense複数の非同期依存をまとめて待つ built-in component