Vue Suspenseコンポーネント
のdefaultスロットと#fallbackスロット、非同期コンポーネントの読み込み、Suspenseのネスト
「Vue Suspenseコンポーネント」はCoddyKit上の無料Vue Academyレッスンです。 これはレッスン1/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはVue Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Vue Academyコースには全4レッスンが含まれています。
Suspenseで解決できること
Suspense は、非同期依存関係を持つコンポーネントの読み込み状態を調整する、Vue組み込みのコンポーネントです。
Suspenseがなければ、各非同期コンポーネントがそれぞれ読み込みスピナーを管理します。Suspense を使うと、ネストされた非同期処理をすべて待機し、準備が整うまで単一のフォールバックを表示する境界を1つ宣言できます。
2つのスロット: defaultとfallback
Suspense は、名前付きスロットを2つ公開します。
- default — 非同期コンポーネントや非同期の
setup()を含められる実際のコンテンツです - fallback — defaultスロットの解決中に表示するコンテンツです
すべての非同期依存関係の処理が完了すると、Vueはフォールバックをdefaultコンテンツに置き換えます。
<template>
<Suspense>
<template #default>
<UserDashboard />
</template>
<template #fallback>
<div class="spinner">Loading dashboard...</div>
</template>
</Suspense>
</template>非同期setup()を依存関係として扱う
コンポーネントの setup() 関数を async として宣言し、何かをawaitすると、そのコンポーネントは非同期依存関係になります。Vueは返されたPromiseを追跡します。
<script setup> での同等の書き方は、ブロック内でトップレベルの await を直接使用することです。
<script setup>
import { ref } from 'vue'
// Top-level await makes this an async dependency of Suspense
const res = await fetch('/api/profile')
const profile = ref(await res.json())
</script>
<template>
<h1>Welcome, {{ profile.name }}</h1>
</template>非同期コンポーネントも対象になる
defineAsyncComponent で作成したコンポーネントも Suspense によって追跡されます。動的な import() が解決されるまで、フォールバックが表示され続けます。
これにより、コンポーネントごとの読み込みUIを記述せずに、サイズの大きいコンポーネントをコード分割できます。
<script setup>
import { defineAsyncComponent } from 'vue'
const Chart = defineAsyncComponent(() =>
import('./HeavyChart.vue')
)
</script>
<template>
<Suspense>
<Chart />
<template #fallback>Preparing chart...</template>
</Suspense>
</template>複数の非同期子コンポーネントをまとめて待機
defaultスロットに複数の非同期依存関係が含まれている場合、Suspenseはすべての解決が完了してからコンテンツを表示します。
これにより、コンポーネントが一つずつ表示されることによるレイアウトシフトを防げます。
<template>
<Suspense>
<div>
<UserStats /> <!-- async setup -->
<RecentOrders /> <!-- async setup -->
<Recommendations /> <!-- async component -->
</div>
<template #fallback>
<PageSkeleton />
</template>
</Suspense>
</template>onErrorCapturedでrejectを捕捉する
非同期のsetup()がrejectされた場合(たとえばfetchに失敗した場合)、エラーは親コンポーネント内で最も近いonErrorCapturedフックへ伝播します。
Suspense自体はエラーを描画しないため、エラーバウンダリと組み合わせます。
<script setup>
import { ref, onErrorCaptured } from 'vue'
const error = ref(null)
onErrorCaptured((err) => {
error.value = err
return false // stop the error from propagating further
})
</script>エラーバウンダリとSuspenseのパターン
本番環境でよく使われるパターンでは、エラーバウンダリコンポーネントの内側にSuspenseを配置します。onErrorCapturedがfalseを返すことで伝播を停止し、代わりにエラーUIを表示します。
<script setup>
import { ref, onErrorCaptured } from 'vue'
const err = ref(null)
onErrorCaptured((e) => { err.value = e; return false })
</script>
<template>
<p v-if="err">Failed to load: {{ err.message }}</p>
<Suspense v-else>
<ProfilePanel />
<template #fallback>Loading...</template>
</Suspense>
</template>pendingイベントとresolveイベント
Suspenseは、リッスン可能なライフサイクルイベントを発行します。
- @pending — pending(読み込み中)状態に入ったときに発生
- @resolve — defaultスロットの解決が完了したときに発生
- @fallback — fallbackコンテンツが表示されたときに発生
アナリティクスやプログレスバーに役立ちます。
<template>
<Suspense @pending="onPending" @resolve="onResolve">
<RouteView />
<template #fallback>Loading route...</template>
</Suspense>
</template>遅延フォールバック用のtimeoutプロパティ
timeoutプロパティ(ミリ秒単位)は、フォールバックの表示を遅らせます。非同期処理がtimeoutより速く解決すれば、フォールバックは表示されません。
これにより、応答が速い場合にスピナーが一瞬だけ表示されるのを防げます。
<template>
<!-- Only show fallback if loading exceeds 200ms -->
<Suspense :timeout="200">
<FastPanel />
<template #fallback>Loading...</template>
</Suspense>
</template>RouterとTransitionでSuspenseを使う
SuspenseはRouterViewおよびTransitionと組み合わせて、非同期のルート変更をアニメーション表示できます。順序が重要です。TransitionでSuspenseをラップし、そのSuspenseでルーティングされたコンポーネントをラップします。
<template>
<RouterView v-slot="{ Component }">
<Transition mode="out-in">
<Suspense>
<component :is="Component" />
<template #fallback>Loading page...</template>
</Suspense>
</Transition>
</RouterView>
</template>Suspenseを使わない場合
コンポーネントがマウントされたまま頻繁に再取得されるデータには、Suspenseを使わないでください。Suspenseが制御するのは初回の非同期解決だけです。継続的な読み込み状態(ページネーションや再取得)には、通常のリアクティブなloading refを使います。
<script setup>
import { ref } from 'vue'
const loading = ref(false)
async function loadMore() {
loading.value = true
await fetchNextPage()
loading.value = false
}
</script>理解度チェック
Suspenseについての理解度を確認しましょう。
まとめ
Suspenseが非同期依存関係をどのように調整するかを学びました。
- defaultスロットには非同期コンテンツ(非同期setupまたは非同期コンポーネント)を配置します
- 読み込み中はfallbackスロットを表示します
- コンテンツを表示する前に、すべての非同期子コンポーネントを待機します
- rejectされたエラーは、親のエラーバウンダリにある
onErrorCapturedへバブルアップします timeoutはスピナーの一瞬の表示を防ぎ、@resolveなどのイベントはアナリティクスに役立ちます
よくある質問
「Vue Suspenseコンポーネント」レッスンは無料ですか?
はい。「Vue Suspenseコンポーネント」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Vue Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Vue Academyコースには全4レッスンが含まれています。
「Vue Suspenseコンポーネント」で何を学びますか?
のdefaultスロットと#fallbackスロット、非同期コンポーネントの読み込み、Suspenseのネスト ブラウザで直接実行するハンズオンコードでVue Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Vue Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのVue Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン1/4です。
「Vue Suspenseコンポーネント」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このVue Academyレッスンでコードを書いて実行できますか?
はい。すべてのVue Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- Vue Suspenseコンポーネント
- defineAsyncComponentによる非同期コンポーネント
- renderToWebStreamによるストリーミングSSR
- 遅延ハイドレーション戦略