3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Vuetifyのv-intersectを使用してライブラリなしで「無限スクロール」を実装する

Last updated at Posted at 2021-12-17

はじめに

Vueで無限スクロールと言えばvue-infinite-loadingが一番よく使われていると思います。
実際に「Vue 無限スクロール」などで検索すると、↑を使用した実装例が殆どです。

今回は、上記のライブラリを使わずに、Vuetifyのコンポーネントを使用して無限スクロールを実装する方法を紹介します。

表題で「ライブラリなし」と言っているのは、vue-infinite-loadingなどの無限スクロール用の外部ライブラリを使わないことを意味しており、UIフレームワークであるVuetifyの使用を前提とした記事となっているのでご注意ください:bow:

環境

vue: 2.6.11
vuetify: 2.4.0

結論

いきなりですが、最終的に書いたコードです。
便宜上、全ての処理をComponent内に書いておりますorz

SampleComponent.vue

<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が呼ばれ)、ローディングを表示することで擬似的な無限スクロールを実現しています。

参考: Intersection observer

まとめ

ライブラリなしでも簡単に実装できました。
(ちなみに冒頭に述べたライブラリを使用しても簡単ですw

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?