【Vue】TypeScript + vue-intersect で遅延ロードを実装する

■ はじめに

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