🌐

Nuxt3でFCM・PWAを使ったアプリで得た知見

2023/07/04に公開

今回は、Nuxt3でFCMとPWAを使った実装を行うことがあったので、そのときに得た知見を残して起きたいと思います。Nuxt3とFCMで実装した記事が少なかったので、自分なりの知見を書いて見ました。

自分の環境と前提条件

"node": "18.14.2",
"npm": "9.5.1",
"yarn": "1.22.19",
"firebase": "^9.17.1",
"firebase-admin": "^11.5.0",
"firebase-functions": "^4.2.1",

使ったmoduleの紹介

Nuxt3でPWAを実装しようと思ったら、公式の@nuxtjs/pwaはNuxt3に対応してないので、下記のモジュールを使ってみました。
 https://github.com/kevinmarrec/nuxt-pwa-module
 まだ公式のmoduleではないですが、READMEにこういう記述がありました。

State of official module
This module is unofficial but aims to become the next iteration of the official Nuxt PWA module.

日本語訳をしてみると、下記のように書かれていました。

まだ公式ではないけど、次の公式のNuxtPWAのモジュールを狙って、開発されている

公式のPWAモジュールの枠を狙って開発されていると書かれていたので、今回採用して使ってみました。

push通知をクリックしたら、ブラウザではなく、PWAを起動するように実装

swでpushイベントのときにshowNotificationメソッドで通知を表示する.
その後に、通知をクリックしたらインストール済みのPWAを起動するようにしたい。
結論: PWAに登録しているserviceWorkerと同じ場所でscopeを設定する。

nuxt.config.ts
pwa:{
  manifest: {
     scope: "/", // ここは登録しているserviceWorkerを同じじゃないとPWAではなく、ブラウザで起動するようになる
  }
}

serviceWorkerでFirebaseSDKを読み込み方法

今はCDNでもv9バージョンが出ているので、最新を使いたい人はそちらをお使ってください!自分はv8で
importSCriptで読み込むだけ

firebase-messaging-sw.js
importScripts("https://www.gstatic.com/firebasejs/8.1.0/firebase-app.js");
importScripts("https://www.gstatic.com/firebasejs/8.1.0/firebase-messaging.js");

新しいリリースがあったときの場合

新しいリリースがあるときはFirestoreにdeleteChacheみたいなフラグを持つことで、フロント側でdeleteChacheがtrueだと更新モーダルを出して、buttonをクリックさせることで、下記のコードを実行することで、ブラウザのキャッシュを削除するようにしています。下記は、実装の一例です。

deleteCacheModal.vue
// serviceWorkerの登録削除
  navigator.serviceWorker.getRegistrations().then(function (registrations) {
    for (const registration of registrations) {
      registration.unregister();
    }
  });
  // キャッシュストレージの削除
  caches.keys().then(function (keys) {
    const promises = [];
    keys.forEach(function (cacheName) {
      if (cacheName) {
        promises.push(caches.delete(cacheName));
      }
    });
  });

参考資料
https://developer.mozilla.org/ja/docs/Web/API/Cache

FCMのSDKのonMessageonBackgroundMessageを使わずに、自分はなんで今回sw.jsで全部実装したか?

serviceWorker側で通知系の処理を全て書いている方が後から読みやすいかなと思ったことからです。次にやる機会があれば、onMessageonBackGroundMessageを使ってみたいです。
下記主要な部分を抜粋した実装例です。

importScripts("https://www.gstatic.com/firebasejs/8.1.0/firebase-app.js");
importScripts("https://www.gstatic.com/firebasejs/8.1.0/firebase-messaging.js")

firebase.initializeApp({
// ここに環境変数を追加する
});

const messaging = firebase.messaging();

// WEBアプリがバックグラウンドの場合にはonBackGroundMessageが呼び出される。
messaging.onBackgroundMessage(function (payload) {
  const notificationTitle = payload.notification.title;
  const notificationOptions = {
    body: payload.notification.body,
  };
  self.registration.showNotification(notificationTitle, notificationOptions);
});
// firebaseのonMessageを使わずにpushイベントがあったら、通知を表示する
self.addEventListener("push", (e) => {
  const notificationData = e.data.json();
  const notificationTitle = notificationData.notification.title;
  const notificationOptions = {
    body: notificationData.notification.body,
    data: notificationData.data,
  };
  self.registration.showNotification(notificationTitle, notificationOptions);
});
// 通知をクリックした時に呼ばれる
self.addEventListener("notificationclick", (e) => {
  let link = ""
  e.waitUntil(clients.openWindow(e.notification.data.link));
  e.notification.close();
});

複数人通知するときにこまったポイント

JavaScriptだけで実装しようとしたが、個人のデバイスのFCMtoken(今後はtokenと表記する)送りたいグループでトピックを作って、グループで登録しているtokenを発行して、そして、そのtokenを使って、通知を送る。だけど、グループの人が変わったりしたら、新しいグループでトピックを作って、tokenを発行したりしないといけなくて、そのとき個人のtokenの発行やトピックのtokenをミスったりすると、通知がいかない事故になるので、複数に通知するなら、(Firebaseを使うなら)functionsでAPIを作って、バックエンドから通知したい複数人のtokenをバックエンドに投げて、それを配列で持って、sendAllメソッドを使う方が二重でtoken管理をしなくて済むので、今回はこの方法を採用しました。

参考資料
https://firebase.google.com/docs/cloud-messaging/js/device-group?hl=ja

https://firebase.google.com/docs/cloud-messaging/send-message?hl=ja#send-messages-to-multiple-devices

Discussion