Häufige Benachrichtigungsmuster

Matt Gaunt

Wir sehen uns nun einige gängige Implementierungsmuster für Web-Push an.

Dazu müssen verschiedene APIs verwendet werden, die im Service Worker verfügbar sind.

Benachrichtigungs-Schließen-Ereignis

Im letzten Abschnitt haben wir gesehen, wie wir auf notificationclick-Ereignisse warten können.

Es gibt auch ein notificationclose-Ereignis, das aufgerufen wird, wenn der Nutzer eines Ihrer Benachrichtigungen. Der Nutzer klickt z. B. auf das Kreuz oder wischt über die Benachrichtigung entfernt).

Dieses Ereignis wird normalerweise für Analysen verwendet, um die Nutzerinteraktionen mit Benachrichtigungen zu erfassen.

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

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

Daten zu einer Benachrichtigung hinzufügen

Beim Empfang einer Push-Nachricht sind Daten häufig nur wenn der Nutzer auf die Benachrichtigung geklickt hat. Beispiel: Die URL das geöffnet werden soll, wenn auf eine Benachrichtigung geklickt wird.

Dies ist die einfachste Methode, um Daten aus einem Push-Ereignis an ein Benachrichtigung besteht darin, dem übergebenen Optionsobjekt einen data-Parameter hinzuzufügen in showNotification() ein, z. B. so:

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);

In einem Klick-Handler kann mit event.notification.data auf die Daten zugegriffen werden.

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('');

Fenster öffnen

Eine der häufigsten Reaktionen auf eine Benachrichtigung ist das Öffnen einer Fenster / Tab zu einer bestimmten URL führt. Das können wir mit dem clients.openWindow() der API erstellen.

In unserem notificationclick-Ereignis würden wir folgenden Code ausführen:

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

Im nächsten Abschnitt sehen wir uns an, wie Sie prüfen können, ob die Seite, auf die der Nutzer weitergeleitet werden soll, eine E-Mail-Adresse ist, oder nicht. So können wir den geöffneten Tab fokussieren, anstatt einen neuen Registerkarten.

Fokus auf ein vorhandenes Fenster verschieben

Wenn möglich, sollten wir ein Fenster fokussieren, anstatt jedes Mal ein neues Fenster zu öffnen, wenn der Nutzer auf eine Benachrichtigung klickt.

Bevor wir uns ansehen, wie dies erreicht werden kann, möchte ich noch einmal darauf hinweisen, ist nur für Seiten auf Ihrem Ursprungsserver möglich. Das liegt daran, dass wir nur sehen, welche Seiten zu unserer Website gehören. Dadurch wird verhindert, dass Entwickler alle Websites sehen können, die von ihren Nutzern aufgerufen werden.

Im vorherigen Beispiel ändern wir den Code, um zu sehen, /demos/notification-examples/example-page.html ist bereits geöffnet.

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);

Sehen wir uns den Code im Detail an.

Zuerst parsen Sie unsere Beispielseite mithilfe der URL API. Das ist ein hübscher Trick, den ich von Jens aufgegriffen habe. Posnick. Wenn Sie new URL() mit dem location-Objekt aufrufen, eine absolute URL zurückgeben, wenn die übergebene Zeichenfolge relativ ist (d.h. / wird zu https://example.com/.

Wir setzen die URL als absolute URL, damit wir sie später mit den Fenster-URLs abgleichen können.

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

Dann erhalten wir eine Liste der WindowClient-Objekte. Dies ist die Liste der aktuell geöffneten Tabs und Fenster. (Beachten Sie, dass diese Tabs nur für Ihren Ursprung sind.)

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

Die an matchAll übergebenen Optionen teilen dem Browser mit, dass nur um nach „Fenster“ zu suchen Typ Clients verwenden (z.B. nach Tabs und Fenstern suchen) und Web Worker ausschließen). Mit includeUncontrolled können wir nach Alle Tabs Ihres Ursprungs, die nicht vom aktuellen Dienst verwaltet werden Worker, d.h. der Service Worker, der diesen Code ausführt. Im Allgemeinen includeUncontrolled muss beim Aufrufen von matchAll() immer „true“ sein.

Das zurückgegebene Promise wird als promiseChain erfasst, damit es an event.waitUntil(), wodurch unser Service Worker aktiv bleibt.

Wenn das matchAll()-Promise aufgelöst wird, durchlaufen wir die zurückgegebenen Window-Clients und ihre URLs mit der URL vergleichen, die wir öffnen möchten. Wenn wir eine Übereinstimmung finden, konzentrieren wir uns darauf, um die Aufmerksamkeit der Nutzenden auf dieses Fenster zu lenken. Die Fokussierung erfolgt über die matchingClient.focus() Anruf.

Finden wir keinen passenden Kunden, öffnen wir wie im vorherigen Abschnitt ein neues Fenster.

.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);
  }
});

Benachrichtigungen zusammenführen

Wir haben festgestellt, dass das Hinzufügen eines Tags zu einer Benachrichtigung eine vorhandene Benachrichtigung mit demselben Tag ersetzt.

Mit der Funktion zum Minimieren von Benachrichtigungen mithilfe der Funktion Notifications API Stellen Sie sich eine Chat-App vor, bei der der Entwickler eine neue Benachrichtigung erhalten möchte. eine Nachricht wie „Du hast zwei Nachrichten von Matt“ anzeigen. und nicht nur die neuesten angezeigt.

Sie können dies tun oder aktuelle Benachrichtigungen auf andere Weise bearbeiten, indem Sie die registration.getNotifications() API, die Ihnen Zugriff auf alle derzeit sichtbaren Benachrichtigungen für Ihre Web-App gewährt.

Sehen wir uns an, wie wir mit dieser API das Chat-Beispiel implementieren können.

Nehmen wir in unserer Chat-App an, dass jede Benachrichtigung Daten enthält, darunter auch einen Nutzernamen.

Als Erstes suchen wir nach offenen Benachrichtigungen für einen Nutzer mit einer bestimmten Nutzernamen. Wir rufen registration.getNotifications() ab, durchlaufen sie und prüfen die notification.data für einen bestimmten Nutzernamen:

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;
});

Als Nächstes wird diese Benachrichtigung durch eine neue ersetzt.

In dieser Fake-Nachrichten-App erfassen wir die Anzahl der neuen Nachrichten, indem wir und erhöhen ihn mit jeder neuen Benachrichtigung.

.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
  );
});

Wenn derzeit eine Benachrichtigung angezeigt wird, erhöhen wir die Anzahl der Nachrichten und legen die Benachrichtigungstitel und Nachrichtentext entsprechend. Wenn es keine Benachrichtigungen haben, wird eine neue Benachrichtigung mit einer newMessageCount von 1 erstellt.

Das Ergebnis ist, dass die erste Nachricht wie folgt aussehen würde:

Erste Benachrichtigung ohne Zusammenführen.

Eine zweite Benachrichtigung würde die Benachrichtigungen wie folgt minimieren:

Zweite Benachrichtigung mit Zusammenführen.

Das Tolle an diesem Ansatz ist, dass, wenn die Nutzer werden Benachrichtigungen übereinander angezeigt, als einfach nur die Benachrichtigung durch die neueste Nachricht zu ersetzen.

Ausnahme von der Regel

Ich habe weiterhin darauf hingewiesen, dass Sie eine Benachrichtigung anzeigen müssen, wenn Sie eine Push-Benachrichtigung erhalten, meisten „true“. Wenn Sie keine Benachrichtigung anzeigen müssen, wenn die Website des Nutzers offen und zielgerichtet ist.

Innerhalb Ihres Push-Ereignisses können Sie überprüfen, ob eine Benachrichtigung angezeigt werden muss, indem Sie Sie untersuchen die Fenster-Clients und suchen nach einem fokussierten Fenster.

Der Code zum Abrufen aller Fenster und zum Suchen eines fokussierten Fensters sieht wie folgt aus:

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;
    });
}

Wir verwenden clients.matchAll() um alle Fenster-Clients abzurufen. Anschließend durchlaufen wir sie und prüfen den focused-Parameter.

Innerhalb unseres Push-Ereignisses würden wir diese Funktion verwenden, um zu entscheiden, ob wir eine Benachrichtigung anzeigen müssen:

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);

Nachrichten an eine Seite über ein Push-Ereignis senden

Sie können die Anzeige einer Benachrichtigung überspringen, wenn sich der Nutzer gerade auf Ihrer Website befindet. Aber was ist, wenn Sie den Nutzer trotzdem über ein Ereignis informieren möchten, aber eine Benachrichtigung zu schwerhändig?

Eine Möglichkeit besteht darin, eine Nachricht vom Service Worker an die Seite zu senden. können dem Nutzer eine Benachrichtigung oder ein Update anzeigen, um ihn über das Ereignis zu informieren. Dies ist nützlich für in denen eine subtile Benachrichtigung auf der Seite für den Nutzer besser und nutzerfreundlicher ist.

Nehmen wir an, wir haben eine Push-Benachrichtigung erhalten, geprüft, können wir „eine Nachricht posten“, zu jeder geöffneten Seite hinzu. Beispiel:

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);

Auf jeder Seite erfassen wir Nachrichten, indem wir ein Nachrichtenereignis hinzufügen. Listener:

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

In diesem Nachrichten-Listener können Sie alles tun, eine benutzerdefinierte UI auf oder ignorieren Sie die Benachrichtigung vollständig.

Wenn Sie auf Ihrer Webseite keinen Nachrichten-Listener definieren, Nachrichten vom Service Worker nichts tun.

Seite im Cache speichern und Fenster öffnen

Ein Szenario, das in diesem Leitfaden nicht behandelt wird, aber erörtert werden sollte, ist, dass Sie die Nutzererfahrung Ihrer Webanwendung im Cache zu verbessern, indem Webseiten im Cache gespeichert werden, die Nutzer danach besuchen sollen auf die Benachrichtigung.

Dazu muss Ihr Service Worker fetch-Ereignisse verarbeiten. Wenn Sie jedoch einen fetch-Event-Listener implementieren, müssen Sie im push-Ereignis nutzen, indem die Seite und die Assets im Cache gespeichert werden die Sie benötigen, damit die Benachrichtigung angezeigt wird.

Browserkompatibilität

Das notificationclose-Ereignis

Unterstützte Browser

  • 50
  • 17
  • 44
  • 16

Quelle

Clients.openWindow()

Unterstützte Browser

  • 40
  • 17
  • 44
  • 11.1

Quelle

ServiceWorkerRegistration.getNotifications()

Unterstützte Browser

  • 40
  • 17
  • 44
  • 16

Quelle

clients.matchAll()

Unterstützte Browser

  • 42
  • 17
  • 54
  • 11.1

Quelle

Weitere Informationen finden Sie in dieser Einführung in Service Worker Blogpost.

Weitere Informationen

Code labs