创建计划推送通知

Avatar of Nico Martin
Nico Martin

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 200 美元的免费积分!

计划是这里的关键词——这是一个相当新的功能!当推送通知被计划(例如,“服药”或“您将在 3 小时后起飞”)时,这意味着即使用户离线,也可以向用户显示它。这与过去有所改进,过去推送通知需要用户在线。

那么计划推送通知是如何工作的呢?我们将重点关注四个关键部分

  • 注册服务工作线程
  • 添加和删除计划推送通知
  • 使用操作按钮增强推送通知
  • 在服务工作线程中处理推送通知

首先,一些背景知识

推送通知是告知网站用户发生某些重要事件并且他们可能希望再次打开我们的(网络)应用程序的好方法。使用 通知 API——结合 推送 APIHTTP Web 推送协议——网络成为了一种从服务器向应用程序发送推送通知并在设备上显示它的简单方法。

您可能已经看到此类功能的发展。例如,您多久会看到某种允许您接受来自网站的通知的警报?虽然浏览器供应商已经在努力寻找解决方案来减少这种烦人的体验(FirefoxChrome 都已概述了计划),Chrome 80 刚刚开始为新的 通知触发器 API 进行源代码试用,它允许我们创建由不同事件触发的通知,而不仅仅是服务器推送。但是,目前,基于时间的触发器是我们拥有的唯一受支持的事件。但是,其他事件,例如基于地理位置的触发器,已经在计划中。

在 JavaScript 中计划事件非常简单,但存在一个问题。在我们的推送通知场景中,我们无法确定应用程序是否在我们想要显示通知的确切时间运行。这意味着我们不能只在应用程序层对其进行计划。相反,我们需要在服务工作线程级别进行。这就是新 API 发挥作用的地方。

通知触发器 API 处于早期反馈阶段。您需要在 Chrome 中启用 #enable-experimental-web-platform-features 标志,或者您应该为源代码试用注册您的应用程序。

此外,服务工作线程 API 需要通过 HTTPS 进行安全连接。因此,如果您在您的机器上尝试它,您需要确保它通过 HTTPS 提供服务。

设置

我创建了一个非常基本的设置。我们有一个 application.js 文件,一个 index.html 文件和一个 service-worker.js 文件,以及一些图像资源。

/project-folder
├── index.html
├── application.js
├── service-worker.js
└── assets
   ├─ badge.png
   └── icon.png

您可以在 GitHub 上找到基本 通知触发器 API 演示 的完整示例。

注册服务工作线程

首先,我们需要注册一个服务工作线程。目前,它除了记录注册成功之外,什么也不做。

// service-worker.js
// listen to the install event
self.addEventListener('install', event => console.log('ServiceWorker installed'));
<!-- index.html -->
<script>
  if ('serviceWorker' in navigator) {
    navigator.serviceWorker.register('/service-worker.js');
  }
</script>

设置推送通知

在我们的应用程序中,我们需要请求用户的权限来显示通知。从那里,我们将获得服务工作线程注册并为该范围注册一个新的通知。到目前为止,没有什么新东西。

有趣的部分是新的 showTrigger 属性。这使我们能够定义显示通知的条件。目前,我们想要添加一个新的 TimestampTrigger,它接受一个时间戳。并且由于所有事情都直接发生在设备上,因此它也可以在离线状态下工作。

// application.js
document.querySelector('#notification-button').onclick = async () => {
  const reg = await navigator.serviceWorker.getRegistration();
  Notification.requestPermission().then(permission => {
    if (permission !== 'granted') {
      alert('you need to allow push notifications');
    } else {
      const timestamp = new Date().getTime() + 5 * 1000; // now plus 5000ms
      reg.showNotification(
        'Demo Push Notification',
        {
          tag: timestamp, // a unique ID
          body: 'Hello World', // content of the push notification
          showTrigger: new TimestampTrigger(timestamp), // set the time for the push notification
          data: {
            url: window.location.href, // pass the current url to the notification
          },
          badge: './assets/badge.png',
          icon: './assets/icon.png',
        }
      );
    }
  });
};

处理通知

现在,通知应该在指定的时间戳显示。但是现在我们需要一种与之交互的方法,这就是我们需要服务工作线程 notificationclicknotificationclose 事件的地方。

这两个事件都侦听相关的交互,并且都可以使用服务工作线程的全部功能。例如,我们可以打开一个新窗口

// service-worker.js
self.addEventListener('notificationclick', event => {
  event.waitUntil(self.clients.openWindow('/'));
});

这是一个非常简单的示例。但是凭借服务工作线程的功能,我们可以做更多的事情。让我们检查所需的窗口是否已打开,并且仅在它未打开时才打开一个新窗口。

// service-worker.js
self.addEventListener('notificationclick', event => {
  event.waitUntil(self.clients.matchAll().then(clients => {
    if (clients.length){ // check if at least one tab is already open
      clients[0].focus();
    } else {
      self.clients.openWindow('/');
    }
  }));
});

通知操作

另一种促进与用户交互的好方法是在通知中添加预定义的操作。例如,我们可以让他们选择是否要关闭通知或打开应用程序。

// application.js
reg.showNotification(
  'Demo Push Notification',
  {
    tag: timestamp, // a unique ID
    body: 'Hello World', // content of the push notification
    showTrigger: new TimestampTrigger(timestamp), // set the time for the push notification
    data: {
      url: window.location.href, // pass the current url to the notification
    },
    badge: './assets/badge.png',
    icon: './assets/icon.png',
    actions: [
      {
        action: 'open',
        title: 'Open app’
      },
      {
        action: 'close',
        title: 'Close notification',
      }
    ]
  }
);

现在我们在服务工作线程中使用这些通知。

// service-worker.js
self.addEventListener('notificationclick', event => {
  if (event.action === 'close') {
    event.notification.close();
  } else {
    self.clients.openWindow('/');
  }
});

取消推送通知

也可以取消挂起的通知。在这种情况下,我们需要从服务工作线程获取所有挂起的通知,然后在它们发送到设备之前关闭它们。

// application.js
document.querySelector('#notification-cancel').onclick = async () => {
  const reg = await navigator.serviceWorker.getRegistration();
  const notifications = await reg.getNotifications({
    includeTriggered: true
  });
  notifications.forEach(notification => notification.close());
  alert(`${notifications.length} notification(s) cancelled`);
};

通信

最后一步是使用服务工作线程客户端上的 postMessage 方法设置应用程序和服务工作线程之间的通信。假设我们想通知已经激活的选项卡发生了推送通知点击事件。

// service-worker.js
self.addEventListener('notificationclick', event => {
  event.waitUntil(self.clients.matchAll().then(clients => {
    if(clients.length){ // check if at least one tab is already open
      clients[0].focus();
      clients[0].postMessage('Push notification clicked!');
    } else {
      self.clients.openWindow('/');
    }
  }));
});
// application.js
navigator.serviceWorker.addEventListener('message', event => console.log(event.data));

总结

通知 API 是一项非常强大的功能,可以增强 Web 应用程序的移动体验。由于通知触发器 API 的到来,它得到了非常重要的改进。该 API 仍在开发中,因此现在是尝试它并向 开发人员提供反馈 的最佳时机。

如果您正在使用 Vue 或 React,我建议您查看 我自己的渐进式 Web 应用程序演示。它包括一个使用通知触发器 API 的这两个框架的文档化示例,如下所示