vue-infinite-loading 無限スクロール

Vue

スマートフォンでの閲覧をターゲットにしたサイト等に良く見られるリストの無限スクロールですが、Vue.js で実装する際は、vue-infinite-loading というライブラリを使うと簡単に実装することが可能です。
ここでは vue-infinite-loading 使って無限スクロールの実装方法を解説していきます。

# インストール

npm もしくは yarn で vue-infinite-loading をインストールします。
vue-infinite-loading (opens new window)

npm install vue-infinite-loading # or yarn add vue-infinite-loading

# リストの実装

まず、無限スクロールを実装する前に、リストの表示を実装します。
ここでは、ブログの記事一覧を取得して、タイトルを表示させる想定です。

<template>
  <div class="vue-infinite-loading">
    <ul>
      <li v-for="item in list" :key="`list${item.id}`">
        {{ item.title }}
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  data() {
    return {
      list: [],
    }
  },
}
</script>

まだ記事を取得していないので、何も表示されませんが、これで記事のタイトルを表示するコンポーネントができました。

# vue-infinite-loading の実装

ここから vue-infinite-loading を導入して、記事の取得まで実装していきます。
まずは vue-infinite-loading をインポートし、コンポーネントを設置します。






 




 

 
 
 








<template>
  <div class="vue-infinite-loading">

    ...

    <infinite-loading></infinite-loading>
  </div>
</template>

<script>
import InfiniteLoading from 'vue-infinite-loading'
export default {
  components: {
    InfiniteLoading,
  },
  data() {
    return {
      list: [],
    }
  },
}
</script>

次にinfinite-loadingコンポーネントがスクロール等で画面に表示された際に、記事を取得する仕組みを実装します。






 









 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 




<template>
  <div class="vue-infinite-loading">

    ...

    <infinite-loading @infinite="infiniteHandler"></infinite-loading>
  </div>
</template>

<script>
import InfiniteLoading from 'vue-infinite-loading'
export default {

  ...

  methods: {
    // infinite-loadingが表示された際の処理
    async infiniteHandler($state) {
      // 記事データの取得
      const data = await this.fetchData()
      if (!data) {
        $state.error()
      } else if (data.length) {
        this.list.push(...data)
        $state.loaded()
      } else if (data.length === 0) {
        $state.complete()
      }
    },
    // 記事データ取得処理
    async fetchData() {
      // axios等で記事を取得する 以下、モック用データを生成
      let data = []
      let num = this.list.length
      if (num < 200) { // 最大200件まで
        for (let i = 1; i <= 20; i++) {
          data.push({ id: num + i , title: `記事タイトル${num + i}` })
        }
      }
      return data
    },
  }
}
</script>

まず、infinite-loadingコンポーネントに@infinite="infiniteHandler"を追記し、infinite-loadingが表示された際にinfiniteHandlerメソッドが呼び出されるようにします。

infiniteHandlerメソッドではfetchDataメソッドで記事データを取得しています。本来であれば、axios等で記事を取得しますが、今回はサンプルデータを最大 200 件まで作成し、20 件ずつ記事データを返すようにしています。
データを取得した後は、infinite-loadingのハンドリングです。

infiniteHandlerが呼ばれた際は$stateが戻り値となります。この$stateを使ってinfiniteHandlerが呼び出された後にinfinite-loadingに結果がどうなったか、通知してあげる必要があります。

記事の取得に失敗した場合、datanullが入ってくる事を想定しています。
エラーの際は$state.error()でエラーの通知をします。

記事データが取得できた場合、listに記事データを追加して、$state.loaded()で読み込みが完了して事を通知します。

記事データが全て取得し終えた場合は、$state.complete()で完了を通知します。

この状態によって、infinite-loadingの表示が変わることが確認できるかと思います。

# カスタマイズ

# ローディングアイコン

spinnerプロパティを設定することで、ローディングのアイコンを変える事が可能です。
default spiral circles bubbles waveDotsが指定できる他、slotで独自で用意したローディングコンポーネント等を表示することも可能です。
spinner (opens new window)

<infinite-loading>
    <div slot="spinner">記事を取得中...</div>
</infinite-loading>

# 表示するデータが無い場合の表示

表示するデータが無い場合、デフォルトではNo results :(と表示されますが、slotno-resultsを定義するとカスタマイズ可能です。

<infinite-loading>
    <div slot="no-results">記事がありませんでした。</div>
</infinite-loading>

# データを全て取得し終えた場合の表示

データを全て取得し終えた場合、デフォルトではNo more data :)と表示されますが、slotno-moreを定義するとカスタマイズ可能です。

<infinite-loading>
    <div slot="no-more">全ての記事が表示されました。</div>
</infinite-loading>

# エラーの表示

エラーの場合、デフォルトではOpps, something went wrong :(と表示され、Retry ボタンが表示されますが、sloterrorを定義するとカスタマイズ可能です。

<infinite-loading>
    <div slot="error">記事の取得中にエラーが発生しました。</div>
</infinite-loading>

# サンプル

# コード

<template>
  <div class="vue-infinite-loading">
    <ul>
      <li v-for="item in list" :key="`list${item.id}`">
        {{ item.title }}
      </li>
    </ul>
    <infinite-loading @infinite="infiniteHandler" spinner="bubbles">
      <div slot="no-results">記事がありませんでした。</div>
      <div slot="no-more">全ての記事が表示されました。</div>
      <div slot="error">記事の取得中にエラーが発生しました。</div>
    </infinite-loading>
  </div>
</template>

<script>
import InfiniteLoading from "vue-infinite-loading";
export default {
  components: {
    InfiniteLoading
  },
  data() {
    return {
      list: []
    };
  },
  methods: {
    async infiniteHandler($state) {
      const data = await this.fetchData();
      if (!data) {
        $state.error();
      } else if (data.length) {
        this.list.push(...data);
        $state.loaded();
      } else if (data.length === 0) {
        $state.complete();
      }
    },
    async fetchData() {
      await this.sleep(1500);
      let data = [];
      let num = this.list && this.list.length;
      if (num < 200) {
        for (let i = 1; i <= 20; i++) {
          data.push({ id: num + i, title: `記事タイトル${num + i}` });
        }
      }
      return data;
    },
    sleep(time) {
      return new Promise(resolve => {
        setTimeout(() => {
          resolve();
        }, time);
      });
    }
  }
};
</script>

<style lang="stylus" scoped>
.vue-infinite-loading
  max-height: 240px
  padding 16px
  overflow-y scroll
</style>

# プレビュー


Last Updated: 2021-9-2 12:36:29