使用 Service Worker 管理通知

Kate Jeffreys
Kate Jeffreys

在此 Codelab 中,您将使用服务工作线程来管理通知。此处的说明假定您已熟悉 Service Worker,以及请求通知权限和发送通知的基本知识。 如果您需要复习通知,请参阅通知 API 使用入门 codelab。如需详细了解 Service Worker,请参阅 Matt Gaunt 的 Service Worker 简介

熟悉示例应用和起始代码

首先,在新的 Chrome 标签页中查看实时应用:

  1. 按 `Control+Shift+J`(在 Mac 上,按 `Command+Option+J`)打开开发者工具。
  2. 点击控制台标签页。

  3. 确保在过滤条件框旁边的级别下拉菜单中选择了 Info 选项。

  4. 在实时应用的开发者工具控制台中,您应该会看到一条控制台消息:

    TODO: Implement getRegistration()

    这是您将在此 Codelab 中实现的函数桩的消息。

现在,我们来看一下示例应用的代码。

  1. 请看 public/index.js

    • 您将实现的函数有四个桩:registerServiceWorkergetRegistrationunRegisterServiceWorkersendNotification

    • requestPermission 函数请求用户授予发送通知的权限。如果您完成了“开始使用 Notifications API”Codelab,您会注意到此处使用了其 requestPermission 函数。唯一的区别在于,它现在还会在解决权限请求后更新界面。

    • updateUI 函数会刷新应用的所有按钮和消息。

    • initializePage 函数用于在浏览器中检测 Service Worker 功能,并更新应用界面。

    • 脚本会等待页面加载完毕,然后对其进行初始化。

  2. 打开 public/service-worker.js

    顾名思义,您需要向应用添加代码,以将此文件注册为服务工作器

    虽然该文件尚未被应用使用,但它包含一些启动代码,可在服务工作线程激活时向控制台输出一条消息。

    您将向 public/service-worker.js 添加代码,以便在服务工作线程收到通知时处理通知。

注册 Service Worker

在此步骤中,您将编写在用户点击应用界面中的 Register service worker 时运行的代码。此代码会将 public/service-worker.js 注册为服务工作线程。

  1. 打开 public/index.js。 将 registerServiceWorker 函数替换为以下代码:

    // Use the Service Worker API to register a service worker.
    async function registerServiceWorker() {
      await navigator.serviceWorker.register('./service-worker.js')
      updateUI();
    }
    

    请注意,registerServiceWorker 使用 async function 声明来更方便地处理 promise。这样,您就可以 await Promise 的已解析值。例如,上述函数会等待注册服务工作线程的结果,然后再更新界面。如需了解详情,请参阅 MDN 上的 await

  2. 现在,用户可以注册 Service Worker,您可以获取对 Service Worker 注册对象的引用。在 public/index.js 中,将 getRegistration 函数替换为以下代码:

    // Get the current service worker registration.
    function getRegistration() {
      return navigator.serviceWorker.getRegistration();
    }
    

    上述函数使用 Service Worker API 获取当前 Service Worker 注册(如果存在)。这样可以更方便地获取对服务工作线程注册的引用。

  • 为了完成 service worker 注册功能,请添加用于取消注册 service worker 的代码。将 unRegisterServiceWorker 函数替换为以下代码:

    // Unregister a service worker, then update the UI.
    async function unRegisterServiceWorker() {
      // Get a reference to the service worker registration.
      let registration = await getRegistration();
      // Await the outcome of the unregistration attempt
      // so that the UI update is not superceded by a
      // returning Promise.
      await registration.unregister();
      updateUI();
    }
    

在您查看实时应用的标签页中,重新加载页面。注册 Service Worker取消注册 Service Worker 按钮现在应该可以正常工作了。

向 Service Worker 发送通知

在此步骤中,您将编写在用户点击应用界面中的 Send a notification 时运行的代码。此代码将创建一个通知,检查是否已注册服务工作线程,然后使用服务工作线程的 postMessage 方法将通知发送给该服务工作线程。

打开 public/index.js,并将 sendNotification 函数替换为以下代码:

// Create and send a test notification to the service worker.
async function sendNotification() {
  // Use a random number as part of the notification data
  // (so you can tell the notifications apart during testing!)
  let randy = Math.floor(Math.random() * 100);
  let notification = {
    title: 'Test ' + randy,
    options: { body: 'Test body ' + randy }
  };
  // Get a reference to the service worker registration.
  let registration = await getRegistration();
  // Check that the service worker registration exists.
  if (registration) {
    // Check that a service worker controller exists before
    // trying to access the postMessage method.
    if (navigator.serviceWorker.controller) {
      navigator.serviceWorker.controller.postMessage(notification);
    } else {
      console.log('No service worker controller found. Try a soft reload.');
    }
  }
}

该代码的作用如下:

  • sendNotification 是一个异步函数,因此您可以使用 await 获取对 Service Worker 注册的引用。

  • 服务工作线程的 postMessage 方法用于将数据从应用发送到服务工作线程。如需了解详情,请参阅 MDN 上有关 postMessage 的文档

  • 该代码会先检查是否存在 navigator.serviceWorker.controller 属性,然后再尝试访问 postMessage 函数。如果没有有效的服务工作线程,或者网页已强制刷新(Shift+重新加载),则 navigator.serviceWorker.controllernull。如需了解详情,请参阅 MDN 上的 ServiceWorker 控制器文档

在 Service Worker 中处理通知

在此步骤中,您将在服务工作线程中编写代码,以处理发布给它的消息并向用户显示通知。

打开 public/service-worker.js。将以下代码添加到文件末尾:

// Show notification when received
self.addEventListener('message', (event) => {
  let notification = event.data;
  self.registration.showNotification(
    notification.title,
    notification.options
  ).catch((error) => {
    console.log(error);
  });
});

以下是简要说明:

  • self 是对服务工作线程本身的引用。

  • 虽然服务工作线程现在负责显示通知,但主应用界面仍负责从用户那里获取通知权限。如果未授予权限,则 showNotification 返回的 promise 会被拒绝。上述代码使用 catch 块来避免未捕获的 Promise 拒绝错误,并更妥当地处理此错误。

继续学习本系列中的下一个 Codelab:构建推送通知服务器