一般的な通知パターン

ウェブプッシュの一般的な実装パターンを見ていきます。

そのためには、Service Worker で利用可能ないくつかの API を使用します。

通知のクローズ イベント

前のセクションでは、notificationclick イベントをリッスンする方法を確認しました。

また、ユーザーが広告を閉じたときに呼び出される notificationclose イベントもあります。 通知をクリックせずに、十字アイコンをクリックするか、 。

このイベントは通常、通知に対するユーザー エンゲージメントを追跡するための分析に使用されます。

self.addEventListener('notificationclose', function (event) {
  const dismissedNotification = event.notification;

  const promiseChain = notificationCloseAnalytics();
  event.waitUntil(promiseChain);
});

通知へのデータの追加

push メッセージを受信すると、通常は ユーザーが通知をクリックした場合に有用です。たとえば 通知をクリックしたときに開く必要があります。

push イベントからデータを取得して、 渡されるオプション オブジェクトに data パラメータを追加することが 次のように showNotification() に配置します。

const options = {
  body:
    'This notification has data attached to it that is printed ' +
    "to the console when it's clicked.",
  tag: 'data-notification',
  data: {
    time: new Date(Date.now()).toString(),
    message: 'Hello, World!',
  },
};
registration.showNotification('Notification with Data', options);

クリック ハンドラ内では、event.notification.data を使用してデータにアクセスできます。

const notificationData = event.notification.data;
console.log('');
console.log('The notification data has the following parameters:');
Object.keys(notificationData).forEach((key) => {
  console.log(`  ${key}: ${notificationData[key]}`);
});
console.log('');

ウィンドウを開く

通知への最も一般的な応答として、 ウィンドウ / タブから特定の URL に移動できます。これを行うには、 clients.openWindow() API

notificationclick イベントでは、次のようなコードを実行します。

const examplePage = '/demos/notification-examples/example-page.html';
const promiseChain = clients.openWindow(examplePage);
event.waitUntil(promiseChain);

次のセクションでは、ユーザーの誘導先となるページが 確認できます。そうすれば、新しいタブを開くのではなく、開いているタブにフォーカスを合わせることができます。 できます。

既存のウィンドウにフォーカスする

可能であれば、ユーザーが新しいウィンドウを開くたびにウィンドウをフォーカスするようにします。 通知をクリックする。

これを実現する方法について説明します 配信元のページでのみ可能です。なぜなら、 閲覧できるのは当サイト内のページのうち どのページが開いているかのみですこれにより ユーザーが閲覧しているすべてのサイトを閲覧できないようにします。

前の例の場合、コードを修正して 「/demos/notification-examples/example-page.html」はすでに開いています。

const urlToOpen = new URL(examplePage, self.location.origin).href;

const promiseChain = clients
  .matchAll({
    type: 'window',
    includeUncontrolled: true,
  })
  .then((windowClients) => {
    let matchingClient = null;

    for (let i = 0; i < windowClients.length; i++) {
      const windowClient = windowClients[i];
      if (windowClient.url === urlToOpen) {
        matchingClient = windowClient;
        break;
      }
    }

    if (matchingClient) {
      return matchingClient.focus();
    } else {
      return clients.openWindow(urlToOpen);
    }
  });

event.waitUntil(promiseChain);

コードを詳しく見ていきましょう。

まず、URL API を使用してサンプルページを解析します。これは私が Posnicklocation オブジェクトで new URL() を呼び出すと、 渡された文字列が相対文字列の場合、絶対 URL を返します(つまり、/https://example.com/ など)。

URL を絶対 URL にすることで、後でウィンドウの URL と照合できます。

const urlToOpen = new URL(examplePage, self.location.origin).href;

次に、WindowClient オブジェクトのリストを取得します。これは、 アクセスできます。(これらは配信元専用のタブです)。

const promiseChain = clients.matchAll({
  type: 'window',
  includeUncontrolled: true,
});

matchAll に渡されるオプションにより、ブラウザに対して 「window」を検索してくださいタイプ クライアント(例: タブとウィンドウを検索するだけ) ウェブワーカーを除外しますincludeUncontrolled を使用すると、 現在のサービスで制御されていない、元のすべてのタブ このコードを実行する Service Worker です。通常は matchAll() を呼び出すときに常に includeUncontrolled を true にする必要があります。

返された Promise を promiseChain としてキャプチャし、 後で event.waitUntil() を実行し、Service Worker を存続させます。

matchAll() Promise が解決されると、返されたウィンドウ クライアントを反復処理し、 開きたい URL と比較します一致するものが見つかった場合は そのウィンドウにユーザーの注意が向くようにします。フォーカスは matchingClient.focus() の呼び出し。

一致するクライアントが見つからない場合は、前のセクションと同様に新しいウィンドウが開きます。

.then((windowClients) => {
  let matchingClient = null;

  for (let i = 0; i < windowClients.length; i++) {
    const windowClient = windowClients[i];
    if (windowClient.url === urlToOpen) {
      matchingClient = windowClient;
      break;
    }
  }

  if (matchingClient) {
    return matchingClient.focus();
  } else {
    return clients.openWindow(urlToOpen);
  }
});

通知の統合

通知にタグを追加すると、特定の要素がある場合に、 置換されます。

ただし、 Notifications API。チャットアプリで、デベロッパーが新しい通知を受け取りたい場合について考えてみましょう。 「Matt からのメッセージが 2 件あります」のようなメッセージを表示する最新の脅威を表示するだけでなく 表示されます。

[ registration.getNotifications() この API を使用すると、ウェブアプリで現在表示されているすべての通知にアクセスできます。

この API を使用してチャットの例を実装する方法を見ていきましょう。

このチャットアプリで、各通知にユーザー名を含むデータが含まれているとします。

最初に行うべきことは、特定のスレッドでの 未終了の通知を見つけることです。 表示されます。registration.getNotifications() を取得してループし、 特定のユーザー名の場合は notification.data:

const promiseChain = registration.getNotifications().then((notifications) => {
  let currentNotification;

  for (let i = 0; i < notifications.length; i++) {
    if (notifications[i].data && notifications[i].data.userName === userName) {
      currentNotification = notifications[i];
    }
  }

  return currentNotification;
});

次のステップでは、この通知を新しい通知に置き換えます。

この架空のメッセージ アプリでは、新しいメッセージの数をカウントするために、 新しい通知ごとにインクリメントします。

.then((currentNotification) => {
  let notificationTitle;
  const options = {
    icon: userIcon,
  }

  if (currentNotification) {
    // We have an open notification, let's do something with it.
    const messageCount = currentNotification.data.newMessageCount + 1;

    options.body = `You have ${messageCount} new messages from ${userName}.`;
    options.data = {
      userName: userName,
      newMessageCount: messageCount
    };
    notificationTitle = `New Messages from ${userName}`;

    // Remember to close the old notification.
    currentNotification.close();
  } else {
    options.body = `"${userMessage}"`;
    options.data = {
      userName: userName,
      newMessageCount: 1
    };
    notificationTitle = `New Message from ${userName}`;
  }

  return registration.showNotification(
    notificationTitle,
    options
  );
});

現在表示されている通知がある場合、メッセージ数を増やし、 それに応じて、通知のタイトルと本文のメッセージが作成されます。もし 通知がない場合は、newMessageCount が 1 の新しい通知が作成されます。

その結果、最初のメッセージは次のようになります。

最初の通知(統合されていません)。

2 番目の通知では、通知が次のように折りたたまれます。

統合に関する 2 番目の通知。

この方法の利点は、 通知が重なって表示されるので 最新のメッセージに置き換えるだけではありません。

ルールの例外

プッシュを受け取ったら通知を表示する必要があると言いましたが、 真実に従うことです。通知を表示する必要がないシナリオの一つは、 ユーザーがサイトを開いた状態で フォーカスしている状態を指します

プッシュ イベント内では、通知を表示する必要があるかどうかを ウィンドウ クライアントを調べてフォーカスされているウィンドウを探します。

すべてのウィンドウを取得し、フォーカスされているウィンドウを検索するためのコードは次のようになります。

function isClientFocused() {
  return clients
    .matchAll({
      type: 'window',
      includeUncontrolled: true,
    })
    .then((windowClients) => {
      let clientIsFocused = false;

      for (let i = 0; i < windowClients.length; i++) {
        const windowClient = windowClients[i];
        if (windowClient.focused) {
          clientIsFocused = true;
          break;
        }
      }

      return clientIsFocused;
    });
}

clients.matchAll() を使用します。 すべてのウィンドウ クライアントを取得してループし、focused パラメータを確認します。

push イベント内では、この関数を使用して通知を表示する必要があるかどうかを判断します。

const promiseChain = isClientFocused().then((clientIsFocused) => {
  if (clientIsFocused) {
    console.log("Don't need to show a notification.");
    return;
  }

  // Client isn't focused, we need to show a notification.
  return self.registration.showNotification('Had to show a notification.');
});

event.waitUntil(promiseChain);

push イベントからページにメッセージを送信する

ユーザーが現在サイトにアクセスしている場合は、通知の表示をスキップできることが確認されています。しかし、 イベントが発生したことをユーザーに知らせる必要がある場合に、通知が どうでしょうか

1 つの方法は、Service Worker からページにメッセージを送信します。 ユーザーは、イベントを通知または更新してユーザーに知らせることができます。これは ページにちょっとした通知を表示するほうが、ユーザーにとってわかりやすく、親しみやすい状況です。

プッシュを受け取り、ウェブアプリが現在フォーカスされていること、 メッセージを投稿して各ページに次のように追加します。

const promiseChain = isClientFocused().then((clientIsFocused) => {
  if (clientIsFocused) {
    windowClients.forEach((windowClient) => {
      windowClient.postMessage({
        message: 'Received a push message.',
        time: new Date().toString(),
      });
    });
  } else {
    return self.registration.showNotification('No focused windows', {
      body: 'Had to show a notification instead of messaging each page.',
    });
  }
});

event.waitUntil(promiseChain);

各ページでメッセージ イベントを追加し、メッセージをリッスンしています。 リスナー:

navigator.serviceWorker.addEventListener('message', function (event) {
  console.log('Received a message from service worker: ', event.data);
});

このメッセージ リスナーでは、何でもできること、カスタム UI を メッセージを完全に無視することもできます。

また、ウェブページでメッセージ リスナーを定義していない場合は、 メッセージは何も行われません。

ページをキャッシュに保存してウィンドウを開く

このガイドの範囲外ですが、説明する価値のあるシナリオとして、 ユーザーが訪れることが予想されるウェブページをキャッシュに保存することで、ウェブアプリの全体的なユーザー エクスペリエンスを向上させる 通知をクリックします

そのためには、Service Worker で fetch イベントを処理するように設定する必要があります。 ただし、fetch イベント リスナーを実装する場合は、必ず ページとアセットをキャッシュに保存して、push イベントでこれを活用する 通知を表示する前に必要な項目をすべて選択してください。

ブラウザの互換性

notificationclose イベント

対応ブラウザ

  • Chrome: 50。 <ph type="x-smartling-placeholder">
  • Edge: 17。 <ph type="x-smartling-placeholder">
  • Firefox: 44。 <ph type="x-smartling-placeholder">
  • Safari: 16。 <ph type="x-smartling-placeholder">

ソース

Clients.openWindow()

対応ブラウザ

  • Chrome: 40。 <ph type="x-smartling-placeholder">
  • Edge: 17。 <ph type="x-smartling-placeholder">
  • Firefox: 44。 <ph type="x-smartling-placeholder">
  • Safari: 11.1。 <ph type="x-smartling-placeholder">

ソース

ServiceWorkerRegistration.getNotifications()

対応ブラウザ

  • Chrome: 40。 <ph type="x-smartling-placeholder">
  • Edge: 17。 <ph type="x-smartling-placeholder">
  • Firefox: 44。 <ph type="x-smartling-placeholder">
  • Safari: 16。 <ph type="x-smartling-placeholder">

ソース

clients.matchAll()

対応ブラウザ

  • Chrome: 42。 <ph type="x-smartling-placeholder">
  • Edge: 17。 <ph type="x-smartling-placeholder">
  • Firefox: 54。 <ph type="x-smartling-placeholder">
  • Safari: 11.1。 <ph type="x-smartling-placeholder">

ソース

詳しくは、こちらの Service Worker の概要 投稿をご覧ください。

次のステップ

Codelab