【Vuetify3】簡単に無限スクロールを実装する方法(v-intersect)
はじめに
こんにちは、SHIFT の IT ソリューション部に所属している Tanaka です。
今回は Vuetifyのv-intersect を使って、追加ライブラリなしで簡単に無限スクロールを実装する方法を紹介しようと思います。
無限スクロール…ウェブページやアプリケーションの一部で、ユーザーがページの最下部にスクロールすると自動的に新しいコンテンツが読み込まれるという機能のこと。
これにより、ユーザーはページの最下部に到達しても「次のページ」ボタンをクリックすることなく、スムーズにコンテンツを閲覧し続けることができる。
「追加ライブラリなし」とは、Vue.jsにVuetify以外の無限スクロール用ライブラリを追加せずに実装する、という意味。
vue-infinite-loadingやvue-infinite-scrollのような外部ライブラリを使うのも一手ですが、今回は余分な依存関係の削減といった理由で、独自実装する方法を解説します。
サンプルコード
実際の挙動
※noteの仕様上、画質が粗くなっております
環境
Vue: 3.4.0
Vuetify: 3.5.0
コード
<template>
<v-container>
<v-list :height="this.$vuetify.display.height * 0.8">
<v-list-item v-for="(item, index) in displayItems" :key="index">
<v-list-item-title>{{ item.title }}</v-list-item-title>
<v-list-item-subtitle>{{ item.subtitle }}</v-list-item-subtitle>
</v-list-item>
<!-- ユーザーがスクロールしたときに新しいアイテムをロードする -->
<div v-intersect="loadItems"></div>
</v-list>
<!-- データをロード中のときに表示する -->
<v-progress-linear v-if="loading" indeterminate />
</v-container>
</template>
<script>
export default {
data: () => ({
items: [],
displayItems: [],
loading: false,
limit: 10, // 一度に表示するアイテムの数
counter: 1 // ページカウンター
}),
methods: {
// 新しいアイテムをロードするメソッド
loadItems(entries, observer, isIntersecting) {
if (isIntersecting) { // ユーザーがスクロールしたときに新しいアイテムをロード
this.loading = true;
const random = Math.floor(Math.random() * 100) + 500;
setTimeout(() => {
this.counter += 1; // ページカウンターを増やす
this.displayItems = this.items.slice(0, this.limit * this.counter); // 新しいアイテムを表示アイテムの配列に追加
this.loading = false; // データのロードが完了したらフラグを下ろす
}, random);
}
}
},
created() {
// 実際はここでAPIを叩き、itemsにデータを格納する想定
for (let i = 0; i < 500; i++) {
this.items.push({
title: `Item ${i + 1}`,
subtitle: `Subtitle ${i + 1}`
});
}
}
};
</script>
サンプルコードの解説
このサンプルコードでは、Vuetifyのv-intersectを使用して無限スクロールを実装しています。
<v-list-item v-for="(item, index) in displayItems" :key="index">
<v-list-item-title>{{ item.title }}</v-list-item-title>
<v-list-item-subtitle>{{ item.subtitle }}</v-list-item-subtitle>
</v-list-item>
まず、v-listを使用してリストを表示しています。
リストの各アイテムはv-forを使用してdisplayItems配列の各アイテムをループして表示しています。
<div v-intersect="loadItems"></div>
次に、リストの最下部にdiv要素を配置し、v-intersectを使用しています。
このdiv要素が画面内に入ると、v-intersectによってloadItemsメソッドが呼び出されます。
loadItems(entries, observer, isIntersecting) {
if (isIntersecting) { // ユーザーがスクロールしたときに新しいアイテムをロード
this.loading = true;
const random = Math.floor(Math.random() * 100) + 500;
setTimeout(() => {
this.counter += 1; // ページカウンターを増やす
this.displayItems = this.items.slice(0, this.limit * this.counter); // 新しいアイテムを表示アイテムの配列に追加
this.loading = false; // データのロードが完了したらフラグを下ろす
}, random);
}
}
呼び出されたloadItemsメソッドでは、まずloadingフラグをtrueに設定してデータのロードを開始します。
次に、setTimeout関数を使用して一定時間後に、.slice()メソッドを使って新しいアイテムをdisplayItems配列に追加します。
新しいアイテムの追加が完了したら、loadingフラグをfalseに設定してデータのロードを終了します。
<v-progress-linear v-if="loading" indeterminate />
この際、v-progress-linearを使用してデータのロード中にプログレスバーを表示しています。
v-ifによって、loadingフラグがtrueのときだけプログレスバーが表示されます。
キーワード解説
今回の機能実現に必要なキーワードを解説します。
1. v-intersect
v-intersectは、Vuetifyが提供するディレクティブで、要素が画面内に入ると指定したメソッドを呼び出す機能を提供します。これにより、ユーザーがページの最下部にスクロールしたときに新しいアイテムをロードすることができます。
引数isIntersectingによって、監視対象の要素が画面内に入っているかどうかを判定できます。
2. v-progress-linear
v-progress-linearは、Vuetifyが提供するプログレスバーコンポーネントです。データのロード中や処理の進行状況をユーザーに視覚的に示すために使用されます。
3. setTimeout
setTimeoutは、指定した時間(ミリ秒)後に関数を一度だけ実行するためのJavaScriptのメソッドです。非同期処理をシミュレートするために使用されます。
使用方法
setTimeout(() => {
// 指定した時間後に実行される処理
}, 1000); // 1000ミリ秒(1秒)後に実行
4. Array.prototype.slice()
Array.prototype.slice()は、配列の一部を抽出して新しい配列を生成するためのJavaScriptのメソッドです。元の配列は変更されません。
使用方法
const newArray = originalArray.slice(start, end);
引数
start: 抽出を開始するインデックス。省略すると0から開始します。
end: 抽出を終了するインデックス。省略すると配列の末尾まで抽出します。
例
const originalArray = [1, 2, 3, 4, 5];
const newArray = originalArray.slice(1, 3); // [2, 3]
おわりに
今回の記事では、 Vuetifyのv-intersect を使って、追加ライブラリなしで無限スクロールを実装する方法を紹介しました。
この記事が読者の皆様の一助になれば幸いです。
もっと知りたい方はこちら
筆者の過去の記事
お問合せはお気軽に!
SHIFTについて(コーポレートサイト)
https://www.shiftinc.jp/
SHIFTのサービスについて(サービスサイト)
https://service.shiftinc.jp/
SHIFTの導入事例
https://service.shiftinc.jp/case/
お役立ち資料はこちら
https://service.shiftinc.jp/resources/
SHIFTの採用情報はこちら
PHOTO:UnsplashのChristian Wiediger