■ はじめに
https://dk521123.hatenablog.com/entry/2021/01/02/000000
の続き。 前回は、JavaScriptでしか実装しなかったが、 今回は、TypeScript + vue-intersect で遅延ロード(Lazy loading)を 実装してみる。
目次
【1】環境設定 1)vue-intersect のインストール 2) 「declare module 'vue-intersect';」の追加 【2】実装イメージ 1)外観の作りについて 2)ローディングの実装イメージ A) ローディングのトリガー ** B) ローディングの終了条件を考える ** 【3】サンプル
【1】環境設定
前回のおさらいも含めて、、、 TypeScriptを使う際には 「2) 「declare module 'vue-intersect';」の追加」が必要。
1)vue-intersect のインストール
npm install vue-intersect --save
2) 「declare module 'vue-intersect';」の追加
上記1)だけだと、 「import Intersect from 'vue-intersect';」 からエラーがでる その対応として、 xxx.d.ts (今回は「shims-vue.d.ts」)に対して、 「declare module 'vue-intersect';」を追加する
shims-vue.d.ts
declare module '*.vue' { import Vue from 'vue'; export default Vue; } // ★ここを追加 declare module 'vue-intersect';
【2】実装イメージ
* 極力、サーバ負荷が掛からないように (一度に大量のリクエストを行わないように) 余計なリクエストを行わないようにするといった 観点で実装イメージを考える
1)外観の作りについて
* 以下のようにローディング対象とintersectタグを配置する
イメージ
[ローディング対象(表、リストなど)] [intersectタグ]
2)ローディングの実装イメージ
A) ローディングのトリガー
* [intersectタグ] の enterイベントで発火させればいい => enterイベント や 他のイベントの詳細は、以下の関連記事に記載。
https://dk521123.hatenablog.com/entry/2021/01/02/000000
B) ローディングの終了条件を考える
ローディング先のAPIの作りにもよるのが 色々な方法が考えられる。 1) ローディング前に、表示するべき件数が合計で何件あるかを取得する 2) ページングのような作りであれば、リクエストしていて0件になったら終了 など => 終了条件を満たしたら、[intersectタグ]を非表示にして、 enterイベントが発火させないようにする
【3】サンプル
https://bnsgt.hatenablog.com/entry/2019/12/07/141439
を元にTypeScriptで実装してみる。
IntersectDemo.vue
<template> <div> <ul class="lists"> <li v-for="value in displayList" :key="value"> {{ value }} </li> </ul> <intersect @enter="onEnter" @leave="onLeave"> <div id="load-more">Loading...</div> </intersect> </div> </template> <script lang='ts'> import { Component, Vue } from 'vue-property-decorator'; import Intersect from 'vue-intersect'; @Component({ components: { Intersect, }, }) export default class IntersectDemo extends Vue { // 初期の画面に表示するリスト数 private MaxRequestForInit = 10; // ローディング時に追加するリスト数 private MaxRequestToAdd = 5; private idList = [ '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31', '32', '33', '34', '35', ]; // 画面に表示するリスト private displayList = new Array<string>(); private loadCount = 1; private intervalId = 0; private isDone = false; public created() { const requestForinit = ( this.idList.length >= this.MaxRequestForInit ? this.MaxRequestForInit : this.idList.length ); for (let i = 0; i < requestForinit; i += 1) { // 本当は、id から外部APIを使ってデータを取得する this.displayList.push(this.idList[i]); } } onLeave() { if (this.intervalId) { clearInterval(this.intervalId); this.intervalId = 0; } } onEnter() { if (this.isDone || this.intervalId !== 0) { return; } this.intervalId = setInterval(() => { const element = document.getElementById('load-more'); if (element === null) { return; } element.classList.add('is-loaded'); const currentList = []; const requestCount = this.MaxRequestForInit + (this.MaxRequestToAdd * this.loadCount); for (let i = 0; i < requestCount; i += 1) { currentList.push(this.idList[i]); } this.displayList = currentList; this.loadCount += 1; // アイテムを表示し終わったら、ローディング要素を非表示にする if ( this.idList.length - this.MaxRequestToAdd <= this.MaxRequestToAdd * this.loadCount ) { if (this.intervalId) { clearInterval(this.intervalId); } element.classList.add('is-loaded'); this.isDone = true; } else { element.classList.remove('is-loaded'); } }, 1000); } } </script> <style> .is-loaded { display: none; } </style>
参考文献
https://bnsgt.hatenablog.com/entry/2019/12/07/141439
関連記事
Vue ~ 入門編 ~
https://dk521123.hatenablog.com/entry/2020/04/30/000000
vue-intersect / 遅延ロード
https://dk521123.hatenablog.com/entry/2021/01/02/000000
タイマー処理 ~ setTimeout / setInterval etc ~
https://dk521123.hatenablog.com/entry/2021/02/01/000000