Vueの新バージョン3で登場した新機能の一つが、Suspenseです。これは、ネットワークなどの非同期処理実装時において、その処理前やエラーハンドリングを簡単にしてくれる機能になります。使い方をマスターすれば、非同期処理の伴う表示処理が簡潔に書けるようになるでしょう。
これまでの非同期処理の書き方
例えば、非同期処理で取得したデータをもとに、データの一覧表示を行うコンポーネント(DataTable)があったとします。この場合、DataTableは次のような処理を実装すると思います。
- 呼び出された直後に「読み込み中…」というラベルを表示する
- 非同期処理がうまくいった時に、データを一覧表示する
- 非同期処理が失敗した時に、「エラーが発生しました」というラベルを表示する
呼び出し元になる親コンポーネントは、データを一覧表示するコンポーネントがどのようなステータスにあるのかまったく関知しない形になります。もし関知する場合には emit 処理を使ってデータやステータスを親コンポーネントに送信することになるでしょう。
Suspenseの使い方
Suspenseは、子コンポーネントの処理状態によって、親コンポーネント側で表示の処理分けを実現します。
まず、親コンポーネントは次のような形で子コンポーネントを呼び出します。
<Suspense>
<template #default>
<MyAsyncComponent />
</template>
<template #fallback>
<span>読み込み中…</span>
</template>
<template #error>
<h1>読み込み失敗しました</h1>
</template>
</Suspense>
ここで、Suspenseタグには3つのtemplateが定義できます。
必須という訳ではありません。
- #default
レンダリングする内容を定義 - #fallback
非同期処理のレンダリングが完了するまでのコンテンツを定義 - #error
エラー発生時のコンテンツを定義
エラーハンドリングについては、throwするだけです。例外処理が発生すれば、#errorで定義された内容をレンダリングします。
export default {
name: "MyAsyncComponent",
async setup(props) {
try {
await asyncFunction()
} catch (e) {
throw e // 分かりやすさのために try/catchにしていますが、本来不要です
}
}
};
エラーを補足する場合
発生したエラーによって、メッセージを変えたい場合もあるでしょう。その場合、errorCaptureをフックして利用できます。
// ref と onErrorCaptured を利用します
import { ref, onErrorCaptured } from 'vue'
export default {
name: 'App',
setup() {
const error = ref(null);
// エラーハンドリング
onErrorCaptured((e) => {
error.value = e
return true;
});
return { error };
}
}
vue-routerとの組み合わせ
Vueでルーティング処理を提供するVue-Routerと組み合わせて利用することもできます。その場合、次のような記述になります。
<Suspense>
<template #default>
<router-view />
</template>
<template #fallback>
<span>初期表示画面のメッセージです</span>
</template>
<template #error>
<h1>表示中にエラーが発生しました</h1>
</template>
</Suspense>
こうすることで、非同期処理の伴う初期画面表示処理において、適切なメッセージを表示できるようになるでしょう。
サンプル
サンプルコードがviniciuskneves/vue-3-suspense: Vue 3 Suspense example for Vue Doseにて公開されています。
以下の画像のように、非同期処理を行いながらメッセージを変える処理を確認できます。下の二つはエラーメッセージです。
まとめ
Suspenseを使うことで、非同期処理の伴うエラー発生時の表示処理を親コンポーネントでコントロールできるようになります。子コンポーネントでエラーハンドリングをせずに済みますので、実装がシンプルになるはずです。ぜひ使いこなしてください。