はじめに
Vueで無限スクロールと言えばvue-infinite-loadingが一番よく使われていると思います。
実際に「Vue 無限スクロール」などで検索すると、↑を使用した実装例が殆どです。
今回は、上記のライブラリを使わずに、Vuetifyのコンポーネントを使用して無限スクロールを実装する方法を紹介します。
表題で「ライブラリなし」と言っているのは、vue-infinite-loadingなどの無限スクロール用の外部ライブラリを使わないことを意味しており、UIフレームワークであるVuetifyの使用を前提とした記事となっているのでご注意ください
環境
vue: 2.6.11
vuetify: 2.4.0
結論
いきなりですが、最終的に書いたコードです。
便宜上、全ての処理をComponent内に書いておりますorz
<template>
<v-container>
<v-row>
<v-col>
<simple-card v-for="user in users" :user="user" />
<!-- ローディング中はローティングを表示 -->
<v-progress-circular v-if="isLoading" indeterminate />
<div v-else-if="isMoreData" v-intersect="onIntersect">続きを読み込む</div>
<!-- 未表示データがない場合は、何も表示しない -->
</v-col>
</v-row>
</v-container>
</template>
<script lang="ts">
export default class SampleComponent extends Vue {
// スクロール時にAPIを実行したレスポンスを格納する
// User型はimportしてきている前提(省略します)
protected users: User[] = [];
// ローディング表示を管理するFlag
protected isLoading = false;
// まだ表示していないデータが存在するかをFlag
protected isMoreData = true;
onIntersect(
_entries: IntersectionObserverEntry[],
_observer: IntersectionObserver,
isIntersecting: boolean
): void {
// 要素が交差していない場合 = isIntersectingがfalseの場合は何もしない
// v-intersect.quietとすることでも可能(だと思う)
if (!isIntersecting) return;
// APIをcallする前にローディングを表示する
this.isLoading = true;
fetchUsers
.then((res) => {
// 今回は空配列が返ってきたら未表示のデータがないとしている
// この辺りはAPIとの兼ね合いで考える
if (res.data.length === 0) {
// このフラグをfalseにすることで「続きを読み込む」が表示されなくなり、以降はonIntersectが呼ばれないようにしている
this.isMoreData = false;
return;
}
// ローカルstateのusersにレスポンスを注入するとコンポーネントが描画される
this.users.push(...res.data)
})
.catch((err) => {
// 必要であればエラーハンドリングを書く
})
.finally(() => {
// 成功でも失敗でもローディングを非表示にする
this.isLoading = false;
});
}
}
</script>
- 「続きを読み込む」が表示されたタイミングでAPIをcallし、レスポンスが返ってくるまではローディングアイコンを表示する
-
simple-card
が無限スクロールで表示したいコンポーネントを想定 - 余計なpropsやclass、ロジックやimport文は外してます
ポイント
v-progress-circular
ローディングアイコンは、Vuetifyのv-progress-circular
を使用しました。
indeterminate
オプションを用いて常時アニメーションにしています
(描画されている間は常にくるくる回っているイメージです)
参考: v-progress-circular コンポーネント
v-intersect
今回の場合は「続きを読み込む」という要素を最下部に置き、これにv-intersect="onIntersect"
を書くことで、交差(スクロールして表示)したタイミングでAPIを実行し(=onIntersect
が呼ばれ)、ローディングを表示することで擬似的な無限スクロールを実現しています。
まとめ
ライブラリなしでも簡単に実装できました。
(ちなみに冒頭に述べたライブラリを使用しても簡単ですw