Vueの新バージョン3で登場した新機能の一つが、Suspenseです。これは、ネットワークなどの非同期処理実装時において、その処理前やエラーハンドリングを簡単にしてくれる機能になります。使い方をマスターすれば、非同期処理の伴う表示処理が簡潔に書けるようになるでしょう。

これまでの非同期処理の書き方

例えば、非同期処理で取得したデータをもとに、データの一覧表示を行うコンポーネント(DataTable)があったとします。この場合、DataTableは次のような処理を実装すると思います。

  1. 呼び出された直後に「読み込み中…」というラベルを表示する
  2. 非同期処理がうまくいった時に、データを一覧表示する
  3. 非同期処理が失敗した時に、「エラーが発生しました」というラベルを表示する

呼び出し元になる親コンポーネントは、データを一覧表示するコンポーネントがどのようなステータスにあるのかまったく関知しない形になります。もし関知する場合には 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を使うことで、非同期処理の伴うエラー発生時の表示処理を親コンポーネントでコントロールできるようになります。子コンポーネントでエラーハンドリングをせずに済みますので、実装がシンプルになるはずです。ぜひ使いこなしてください。

Vue.js