2020年Q1リリース予定のVue.js 3.0の新機能 Suspense
を試してみたのでまとめます。
かなり使い勝手が良さそうです。
(参考)
以下でVue 3.0(vue-next)の環境構築、他の新機能についてもまとめています。
Suspenseとは?
非同期処理が解決されるまでフォールバックコンテンツ(例えばLoading中アイコン)を表示してくれる特別なコンポーネントです。
いままで、v-if="loading === true"
などの状態変数を使って制御していたものを、状態変数を使わずに簡潔に書くことができます。
※ Reactには既にあるようです。https://reactjs.org/docs/concurrent-mode-suspense.html
※ @kazuponさんの資料によると、まだビルドオプションで制御される機能で、不確定な部分もあるようです。
Suspenseの書き方
以下のように、<Suspense>
コンポーネントでラップして、内部の<template #default>
内に、非同期コンポーネントを、<template #fallback>
内に、その非同期コンポーネントが解決するまでに描画したいもの(フォールバックコンテンツ)を記載します。
それだけで、非同期コンポーネントの処理が解決するまでフォールバックコンテンツを描画してくれるようになります。
この場合だと、<AsyncComponents>
の非同期処理が終わるまで、loading...
という文字列を表示してくれます。
<Suspense>
<template #default>
<AsyncComponents/>
</template>
<template #fallback>
loading...
</template>
</Suspense>
Suspensのサンプル実装
サンプルとして、このGIFのように非同期処理待ちの際にLoading表示をするコンポーネントを作ります。
以下で紹介しているコードはこちらにあります。
https://github.com/kawamataryo/vue-next-ts-webpack-preview/tree/suspense-sample
1. 非同期コンポーネントの作成
まず、非同期処理を行うコンポーネントを作ります。
UserデータをAPIから取得して、User名のリストを表示することを想定しています。
ポイントはsetup()関数でasync/awaitを使いPromiseを返すところです。
これで非同期コンポーネントとして、Suspenseでの待機対象となります。
<template>
<ul>
<li v-for="(user, i) in users" :key="i">{{ user }}</li>
</ul>
</template>
<script lang="ts">
import { defineComponent } from "vue";
export default defineComponent({
async setup() {
// APIリクエストのモック 2000ms後にユーザー名の配列を返す
const fetchUsers = () => {
return new Promise<string[]>(resolve => {
setTimeout(() => {
resolve(['Jon', 'Bob', 'Nancy'])
}, 2000)
})
};
// ユーザー名を取得
// setup()内で読んでいるのでVue.js 2系で言うonCreated()と同じライフサイクルで実施される。
const users = await fetchUsers();
return {
users
}
}
});
</script>
2. Suspense 利用側のコンポーネント
次に、Suspense利用側のコンポーネントを書いていきます。
非同期コンポーネントを<Suspense>
の<template #default>
内に記載して、
<template #fallback>
内に非同期処理が解決するまで描画したいLoading...
を記載しています。
これだけで非同期処理を待つ間はLoadingを表示するという実装が出来ます。
<template>
<img src="./logo.png">
<h1>Suspense demo</h1>
<Suspense>
<template #default>
<AsyncUsers/>
</template>
<template #fallback>
Loading...
</template>
</Suspense>
</template>
<script lang="ts">
import { ref, defineComponent, onErrorCaptured, Ref } from 'vue'
import AsyncUsers from "./components/AsyncUsers.vue"
export default defineComponent({
components: {
AsyncUsers
},
setup() {}
})
</script>
複数の非同期コンポーネントを待つ場合
Suspenseが特に効果を発揮するのが、複数の非同期コンポーネントを待つ場合だと思います。
実装はとても簡単です。<Suspense>
内に処理を待ちたい非同期コンポーネントを列挙すればOKです。
全ての非同期コンポーネントが解決するまで、フォールバックコンテンツを描画してくれます。
<Suspense>
<template #default>
<AsyncUsers/>
<AsyncFoods/>
<AsyncAnimals/>
</template>
<template #fallback>
Loading...
</template>
</Suspense>
以下のサンプルでは、AsyncUsers, AsyncAnimalsが2000ms、AsyncFoodsが3000msの描画待ちで設定しています。
全てが解決するまでloadigを表示してくれてます。
エラーハンドリング
APIリクエストといったらエラーハンドリングが必要ですよね。
Suspense
を使う場合のエラーハンドリングは、Vue3.0で新しく追加されたライフサイクルフックonErrorCaptured
で簡潔にかけます。
以下はAsyncUsers
で発生したエラーをonErrorCaptured
でキャッチして、エラーメッセージを表示する例です。
<template>
<div class="container">
<img src="./logo.png">
<h1>Suspense demo</h1>
<div v-if="error">
{{ error }}
</div>
<Suspense>
<template #default>
<AsyncUsers>
</template>
<template #fallback>
Loading...
</template>
</Suspense>
</div>
</template>
<script lang="ts">
import {defineComponent, onErrorCaptured, ref, Ref} from 'vue'
import AsyncUsers from "./components/AsyncUsers.vue"
export default defineComponent({
components: {
AsyncUsers,
},
setup() {
const error: Ref<any> = ref(null);
// 非同期コンポーネントでErrorが発生した場合、このライフサイクルフックでキャッチされる。
// エラーの内容を、リアクティブなエラー表示用変数の`error`に詰めている
onErrorCaptured(e => {
error.value = e;
return true;
});
return {
error
}
}
})
</script>
AsyncUsers
であえてreject(500 Server Error")を発生させた場合はこのようになります。
終わりに
以上、Vue.js 3.0 の新機能を試す。 〜Suspense編〜 でした
状態変数を持たなくて良いのでとてもシンプルにかけますね。
早く使いたい..!!