【Firebase】FireStoreとVueでページング処理【タイムライン】

前書き

実装の一つのテーマとしてTwitterのタイムラインのような機能が欲しいなと思って実装しました。
FireStoreから取得したデータと画像をタイムライン形式に表示し、
ボタンを押すと、次の投稿をロードできます。

【実装】auth.js(FireStoreとのアクセスを担うスクリプト

export async function getAllData(limit, pagingToken) {
    let nextToken = "";
    let query = db.collection("comments").orderBy('createdAt', 'desc').limit(limit);

    if (pagingToken != "") {
        const [seconds, nanoseconds] = pagingToken.split(':');
        const timestamp = new firebase.firestore.Timestamp(seconds, nanoseconds);
        query = query.startAfter(timestamp);
    }

    const result = await query.get().then((snapshot) => {
        if (snapshot.docs.length >= limit) {
            const last = snapshot.docs[snapshot.docs.length - 1];
            const lastData = last.data();
            const time = lastData.createdAt;
            nextToken = `${time.seconds}:${time.nanoseconds}`;
        }
        return { "BuffData": snapshot, "nextPageToken": nextToken };
    }).catch(() => {
        alert("エラーが発見されました:データ取得時");
    });
    return result;
}

解説

まず投稿処理を記述したスクリプトにFireStoreに格納するデータに日付を保存するよう記述します。
取得時にクエリを発行し、投稿日時順に情報が呼び出されます。
ここでは投稿が新しい順に取得されます。最大3件まで取得します。

let query = db.collection("comments").orderBy('createdAt', 'desc').limit(limit);

ここでは取得した件数がlimit以上であれば、まだ投稿が残っている or 最後の投稿を取得し終えた事になります。
取得したLimit件の投稿の中で最も古い投稿の時間を取得し、nextTokenとして保存します。
これによりnextTokenを頼りに投稿を遡っていきます。

const result = await query.get().then((snapshot) => {
        if (snapshot.docs.length >= limit) {
            const last = snapshot.docs[snapshot.docs.length - 1];
            const lastData = last.data();
            const time = lastData.createdAt;
            nextToken = `${time.seconds}:${time.nanoseconds}`;
        }
        return { "BuffData": snapshot, "nextPageToken": nextToken };
    }).catch(() => {
        alert("エラーが発見されました:データ取得時");
    });

タイムラインにてロードボタンを押した際に、
前回の記事の投稿時間をnextTokenに取得したままですので、
これより前の投稿を取得するstartAfterクエリを追加します。

 if (pagingToken != "") {
        const [seconds, nanoseconds] = pagingToken.split(':');
        const timestamp = new firebase.firestore.Timestamp(seconds, nanoseconds);
        query = query.startAfter(timestamp);
    }

【実装】Home.vue

<v-btn wrap row justify-center v-if="pagingToken" @click="nextPaging();" color="primary">次のページ</v-btn>

ロードボタンは前投稿日時であるpageTokenが存在している(まだ投稿が残っている or ちょうど最後の投稿を取得し終えた)場合に出現します。

  data() {
    return {
      pagingToken: "",
      maps_data: null
    };
  },
  methods: {
    nextPaging: async function() {
      let data = [];
      data = await getAllData(3, this.pagingToken);
      let buffData = await downloadImageToBox(data.BuffData);
      this.maps_data = this.maps_data.concat(buffData);
      this.pagingToken = data.nextPageToken;
    },

ロードボタンを押した際の挙動をmethodsに記載します。
getAllData関数とdownloadImageToBox関数(画像のダウンロード=>詳細は後日)はJSファイルからImportしています。
初期状態ページがマウントされた際はmountedにて記述された処理が実装されます。【ここでは最新3件の情報を取得】
最新3件の情報はmaps_dataオブジェクトに格納されます。
しかしmountedにて取得された情報より前の投稿を取得するにはロードボタンを押さなくてはなりません。
ロードボタンを押すとmethodsのnextPaging()が実行されます。
ここで取得した次の3件(=buffData)が元々タイムラインにて存在していた最新の3件(=maps_data)と結合し、
maps_dataは6件となります。よってmaps_dataはタイムラインにて表示される投稿が格納されるわけです。

watch: {
    maps_data: async function() {}
  },

maps_dataに変更(TLに表示されて欲しい投稿の変更)が確認された場合は、
すぐさまタイムラインへ反映されて欲しいので、指定したデータの変更を検知してくっるwatchプロパティを活用します。

結果

f:id:electric-city:20201215163708p:plain
ロード前
f:id:electric-city:20201215163723p:plain
ロード後

参考

qiita.com

あとがき

無限スクロールも試したいですね。