WebプッシュとVue.js、フロントエンドでのWebプッシュメッセージの操作についてもう一度

次のプロジェクトでWebプッシュメッセージを使用して機能に取り組んだ後、これをすばやく問題なく実行するための十分な情報がまだないことがわかりました。したがって、すべてが私の記憶から消えたわけではありませんが、私はこの経験を記事の形で形式化することを急いでいます。



たとえば、web-push-libs / web-pushライブラリを使用して、比較的低レベルの手段を使用してWebプッシュメッセージを送受信することに焦点を当てた2017〜2018年の記事を見つけることができますこのライブラリはまだ開発中ですが、最近ではFirebaseのライブラリを操作する方がはるかに簡単です。



ファイヤーベースプロジェクトの設定



それでは、firebaseでプロジェクトを作成することから始めましょう。firebaseコンソール開いて、新しいプロジェクトを作成する必要があります。では一般的な情報- >設定>一般設定- >あなたのアプリケーション、新しいWebアプリケーションを作成する必要があります。これにより、フロントエンド側でWebアプリケーション初期化コードが生成されます。



<!-- The core Firebase JS SDK is always required and must be listed first -->
<script src="https://www.gstatic.com/firebasejs/7.19.0/firebase-app.js"></script>

<!-- TODO: Add SDKs for Firebase products that you want to use
     https://firebase.google.com/docs/web/setup#available-libraries -->
<script src="https://www.gstatic.com/firebasejs/7.19.0/firebase-analytics.js"></script>

<script>
  // Your web app's Firebase configuration
  var firebaseConfig = {
    apiKey: "...",
    authDomain: "...",
    databaseURL: "...",
    projectId: "...",
    storageBucket: "...",
    messagingSenderId: "...",
    appId: "...",
    measurementId: "..."
  };
  // Initialize Firebase
  firebase.initializeApp(firebaseConfig);
  firebase.analytics();
</script>


Firebaseコンソールの同じタブ[一般情報]-> [設定]-> [クラウドメッセージング]-> [プロジェクトの資格情報]-> [サーバーキー]に、Firebaseサーバーを介してプッシュ通知を送信できる秘密キーがあります。



Webプッシュメッセージの送信



フロントエンド開発者は、curlコマンドを使用して独自にWebプッシュメッセージを送信できます。



curl -X POST -H "Authorization: key=< >" -H "Content-Type: application/json"    -d '{
    "data": {
        "title": "FCM Message",
        "body": "This is an <i>FCM Message</i>",
        "icon": "/static/plus.png",
        "sound": "/static/push.mp3",
        "click_action": "https://google.com",
  },
  "to": "< >"
}' https://fcm.googleapis.com/fcm/send


サーバーキーの取得については、Firebaseプロジェクトの設定のセクションで説明し、登録トークンの取得については、登録トークンの取得のセクションで説明します



データと通知ペイロード



ペイロードは、Webプッシュメッセージのデータまたは通知フィールドで送信できます。通知ペイロードの場合、要求は次のようになります(データペイロードについては、プッシュメッセージの送信セクションの要求を参照してください)。



curl -X POST -H "Authorization: key=< >" -H "Content-Type: application/json"    -d '{
    "notification": {
        "title": "FCM Message",
        "body": "This is an <i>FCM Message</i>",
        "icon": "/static/plus.png",
        "click_action": "https://google.com",
  },
  "to": "< >"
}' https://fcm.googleapis.com/fcm/send


データと通知ペイロードには、2つの基本的な違いがあります。



  1. 通知ペイロードには厳密に定義されたフィールドのセットがあり、余分なフィールドは無視されますが、データペイロードはすべてのフィールドを制限なしにフロントエンドに送信します。
  2. Webブラウザーがバックグラウンドにあるか、アクティブリンクにサードパーティのサイトが含まれている場合、通知ペイロードWebプッシュは制御をonMessageイベントハンドラーに転送せずにメッセージを表示しますが、データペイロードWebプッシュは常に制御をonMessageイベントハンドラーに転送します。メッセージを表示するには、Notificationオブジェクトを明示的に作成する必要があります。Webブラウザーがアクティブな状態にあり、サイトがアクティブなタブで開いている場合、データと通知ペイロードの処理に違いはありません。


メッセージングオブジェクトの作成



フロントエンドでWebプッシュメッセージを処理するには、メッセージングオブジェクトを作成する必要があります。



const messaging = window.firebase.messaging();


このコードでは、firebaseこれは、firebaseライブラリのロード中に作成され、firebaseプロジェクトの設定で説明されているようにフロントエンド側で初期化されるグローバルオブジェクトですプロジェクトはVue.jsで開発されました。したがって、htmlスクリプト要素を介してスクリプトを接続することは有望に見えませんでした。これらのスクリプトを接続するために、私はライブラリを使用しましたvue-plugin-load-script



import Vue from "vue";
import LoadScript from "vue-plugin-load-script";

Vue.use(LoadScript);

var firebaseConfig = {
  apiKey: "...",
  authDomain: "...",
  databaseURL: "...",
  projectId: "...",
  storageBucket: "...",
  messagingSenderId: "...",
  appId: "...",
  measurementId: "..."
};

Promise.resolve()
  .then(() =>
    Vue.loadScript(
      "https://www.gstatic.com/firebasejs/7.14.0/firebase-app.js"
    )
  )
  .then(() =>
    Vue.loadScript(
      "https://www.gstatic.com/firebasejs/7.14.0/firebase-messaging.js"
    )
  )
  .then(() =>
    Vue.loadScript(
      "https://www.gstatic.com/firebasejs/7.14.0/firebase-analytics.js"
    )
  )
  .then(() => {
    window.firebase.initializeApp(firebaseConfig);
    const messaging = window.firebase.messaging();
    ... //    messaging
  });


登録トークンの取得



登録トークンは、デバイスとWebブラウザーを一意に識別する識別子です。これにより、Webプッシュメッセージを特定のデバイスに送信し、特定のWebブラウザーで処理できるようになります。



  Notification.requestPermission()
    .then(permission => {
      if (permission === "granted") {
        messaging
          .getToken()
          .then(token => {
            ... //    
          });
      } else {
        console.log("Unable to get permission to notify.");
      }
    });


状況によっては、トークンを更新できます。そして、トークン更新イベントを処理する必要があります。



  messaging.onTokenRefresh(function() {
    messaging
      .getToken()
      .then(function(refreshedToken) {
         ... //     
      });
  });


このイベントに関して、私は質問があります-それは関連していますか?事実、FCMに移行する前でも、トークンローテーション手順はGCMで機能していました。これはAndroidライブラリに記述されており、サーバー操作の記述に間接的に記述されていました。各サーバーの応答には正規のトークンが含まれており、それらは常にチェックおよび変更する必要がありました(ただし、私以外では、誰もフォローすることはめったにありませんでした)。FCMに移行した後、正規トークンなどの概念は使用されなくなりました(実際には、ほとんど追跡されなかったためと考えられます)。この点で、イベントが発生する可能性のあるケースは完全には明確ではありませんonTokenRefresh()



OnMessageイベント-簡略化されたバージョン



なぜ単純化されているのか、すぐにお答えします。少なくとも2つの簡略化を行います。1)アプリケーションが追加の作業なしでバックグラウンドにある場合、通知ペイロードを使用してメッセージを受信および表示します。2)モバイルデバイスでは、セキュリティシステムが新しいNotification()演算子の実行を許可しないことを忘れてください。



したがって、すでに述べたように、通知ペイロードの場合、フロントエンド開発者のわずかな参加なしに(もちろん、登録トークンをサーバーに送信した後)Webプッシュメッセージが表示されます。Webブラウザーがアクティブな状態にあり、サイトがアクティブなタブで開いている場合は、まだ解決されていません。



  messaging.onMessage(function(payload) {
      const data = { ...payload.notification, ...payload.data };
      const notificationTitle = data.title;
      const notificationOptions = {
          body: data.body,
          icon: data.icon,
          image: data.image,
          click_action: data.click_action,
          requireInteraction: true,
          data
      };
      new Notification(payload.notification.title, payload.notification);
  });


バックグラウンドでWebプッシュメッセージを受信するイベントの処理



このセクションでは、サービスワーカーとの作業を開始します。そしてこれは、とりわけ、安全なhttpsプロトコルを使用して動作するようにサイトを構成する必要があることを意味します。そして、これはすぐにさらなる開発を複雑にします。したがって、単純なケースでは、すでに前に説明したことで十分です。



firebaseライブラリを操作するには、firebase-messaging-sw.jsという名前のファイル別のファイル名を設定することもできますが、Webブラウザー保護の特性により、いずれの場合もルートディレクトリに配置する必要があります(そうしないと、このサービスワーカーはサイト全体で機能しません)。



原則として、イベントハンドラーもこのファイルに配置されますnotificationclickこのコードと異なるものはほとんど見つかりません。



var firebaseConfig = {
  apiKey: "...",
  authDomain: "...",
  databaseURL: "...",
  projectId: "...",
  storageBucket: "...",
  messagingSenderId: "...",
  appId: "...",
  measurementId: "..."
};

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

firebase.initializeApp(firebaseConfig);
const messaging = firebase.messaging();

messaging.setBackgroundMessageHandler(function(payload) {
  const data = { ...payload.notification, ...payload.data };
  const notificationTitle = data.title;
  const notificationOptions = {
    body: data.body,
    icon: data.icon,
    image: data.image,
    requireInteraction: true,
    click_action: data.click_action,
    data
  };
  self.registration.showNotification(notificationTitle, notificationOptions);
});

self.addEventListener("notificationclick", function(event) {
  const target = event.notification.data.click_action;
  event.notification.close();
  event.waitUntil(
    clients
      .matchAll({
        type: "window",
        includeUncontrolled: true
      })
      .then(function(clientList) {
        for (var i = 0; i < clientList.length; i++) {
          var client = clientList[i];
          console.log(client.url, client.focus);
          if (client.url === target && "focus" in client) {
            return client.focus();
          }
        }
        return clients.openWindow(target);
      })
  );
});


ServiceWorkerでonMessageイベントを処理するオプション



メッセージイベント のセクション(簡略化されたバージョン)で、 Webプッシュメッセージの処理方法についてはすでに説明したことを思い出してくださいしかし、この方法には1つの重大な欠点がありました。それは、Webブラウザー保護システムの特殊性のために、モバイルデバイスでは機能しませんでした。この欠点を克服するために、Notificationオブジェクトがすでに埋め込まれているService Workerオプションが発明され、新しいオペレーターで作成する必要はありません。



  messaging.onMessage(function(payload) {
    play();
    navigator.serviceWorker.register("/firebase-messaging-sw.js");
    Notification.requestPermission(function(result) {
      if (result === "granted") {
        navigator.serviceWorker.ready
          .then(function(registration) {
            const data = { ...payload.notification, ...payload.data };
            const notificationTitle = data.title;
            const notificationOptions = {
              body: data.body,
              icon: data.icon,
              image: data.image,
              click_action: data.click_action,
              requireInteraction: true,
              data
            };
            return registration.showNotification(
              notificationTitle,
              notificationOptions
            );
          })
          .catch(function(error) {
            console.log("ServiceWorker registration failed", error);
          });
      }
    });
  });


Webプッシュメッセージを受信するとビープ音が鳴る



プッシュ通知がさまざまなデバイスでどのように表示されるかについては、実質的に制御できないと言わざるを得ません。これがポップアップメッセージになる場合もあれば、プッシュがすぐにシステムの「プレート」に落ちる場合もあります。それでも音声が機能しない場合は、クライアントに失われます。 svukでは、すべてが非常に困難です。以前の仕様には、以前はWebプッシュメッセージを受信したときの音を担当していた音場が含まれていましたが、現在、そのようなプロパティはありません。この点で、私はプッシュのオーディオ録音を作成するという目標を設定しました。



htmlオーディオ要素の作成とそのplay()メソッドの呼び出しで時々遭遇する説明は、Webブラウザーのセキュリティ機能のために機能しません(実際のユーザーからのクリックでのみ呼び出すことができます)。ただし、AudioContext()もあります。これを使用します。



const play = () => {
  try {
    const context = new AudioContext();
    window
      .fetch(soundUrl)
      .then(response => response.arrayBuffer())
      .then(arrayBuffer => context.decodeAudioData(arrayBuffer))
      .then(audioBuffer => {
        const source = context.createBufferSource();
        source.buffer = audioBuffer;
        source.connect(context.destination);
        source.start();
      });
  } catch (ex) {
    console.log(ex);
  }
};


すべて問題ありませんが、AudioContext()オブジェクトを持たないServiceWorkerがまだあります。すべてのワーカーがメッセージを介して通信することを忘れないでください。そして、サービスワーカーからのイベントの受信は次のようになります。



try {
  const broadcast = new BroadcastChannel("play");
  broadcast.onmessage = play;
} catch (ex) {
  console.log(ex)  ;
}


もちろん、このコードが機能するには、1)ブラウザが開いている2)サイトが開いている必要があります(ただし、必ずしもアクティブなタブにあるとは限りません)。しかし、他に方法はありません。



後書きの代わりに



今、あなたはちょっと息を吐き、言うことができます、それだけです。しかし...これはすべてサファリでは機能しません-そしてこれは別の別個の不十分に文書化されたトピックですが、いくつかの記事が見つかります。



便利なリンク



1)habr.com/ru/post/321924



apapacy@gmail.com

2020年8月24日



All Articles