Codelab:构建推送通知客户端

此 Codelab 逐步演示了如何构建推送通知客户端。完成此 Codelab 后,您将拥有一个客户端,该客户端可以:

  • 让用户订阅推送通知。
  • 接收推送消息并将其显示为通知。
  • 取消用户订阅推送通知。

此 Codelab 侧重于通过实践来帮助您学习,并且不涉及太多概念。请查看推送通知的工作原理?以了解推送通知的概念。

此 Codelab 的服务器代码已完成。您只需在此 Codelab 中实现客户端。要了解如何实现推送通知服务器,请查看 Codelab:构建推送通知服务器

查看 push-notifications-client-codelab-complete源代码)以查看完整代码。

浏览器兼容性

已知此 Codelab 可在以下操作系统和浏览器组合下工作:

  • Windows:Chrome、Edge
  • macOS:Chrome、Firefox
  • Android:Chrome、Firefox

已知此 Codelab 适用于以下操作系统(或操作系统和浏览器组合):

  • macOS:Brave、Edge、Safari
  • iOS

设置

获取代码的可编辑副本

在本 Codelab 中,您在这些说明右侧看到的代码编辑器将称为 Glitch UI

  1. 点击Remix to Edit 以使项目可编辑。

设置身份验证

在推送通知工作之前,您需要使用身份验证密钥设置服务器和客户端。请参阅签署 Web 推送协议请求以了解原因。

  1. 在 Glitch UI 中,点击工具,然后点击终端以打开 Glitch 终端。
  2. 在 Glitch 终端中,运行 npx web-push generate-vapid-keys。复制私钥和公钥值。
  3. 在 Glitch UI 中,打开 .env 并更新 VAPID_PUBLIC_KEYVAPID_PRIVATE_KEY。将 VAPID_SUBJECT 设置为 mailto:test@test.test。所有这些值都应包含在双引号中。进行更新后,您的 .env 文件应类似于这样:
VAPID_PUBLIC_KEY="BKiwTvD9HA…"
VAPID_PRIVATE_KEY="4mXG9jBUaU…"
VAPID_SUBJECT="mailto:test@test.test"
  1. 关闭 Glitch 终端。
  1. 打开 public/index.js
  2. VAPID_PUBLIC_KEY_VALUE_HERE 替换为您的公钥值。

注册 Service Worker

您的客户端最终将需要 Service Worker 来接收和显示通知。最好尽早注册 Service Worker。有关更多上下文,请参阅接收推送消息并将其显示为通知

  1. // TODO add startup logic here 注释替换为以下代码:
// TODO add startup logic here
if ('serviceWorker' in navigator && 'PushManager' in window) {
  navigator.serviceWorker.register('./service-worker.js').then(serviceWorkerRegistration => {
    console.info('Service worker was registered.');
    console.info({serviceWorkerRegistration});
  }).catch(error => {
    console.error('An error occurred while registering the service worker.');
    console.error(error);
  });
  subscribeButton.disabled = false;
} else {
  console.error('Browser does not support service workers or push messages.');
}

subscribeButton.addEventListener('click', subscribeButtonHandler);
unsubscribeButton.addEventListener('click', unsubscribeButtonHandler);
  1. 要预览站点,请按查看应用。然后按全屏 fullscreen
  1. 按 `Control+Shift+J`(在 Mac 上按 `Command+Option+J`)以打开 DevTools。
  2. 点击 Console 选项卡。您应该看到消息 Service worker was registered. 记录到 Console。

请求推送通知权限

您绝不应在页面加载时请求发送推送通知的权限。相反,您的 UI 应询问用户是否希望接收推送通知。一旦他们明确确认(例如,通过点击按钮),您就可以开始从浏览器获取推送通知权限的正式流程。

  1. 在 Glitch UI 中,点击查看源代码以返回到您的代码。
  2. public/index.js 中,将 // TODO 注释替换为 subscribeButtonHandler() 中的以下代码:
// TODO
// Prevent the user from clicking the subscribe button multiple times.
subscribeButton.disabled = true;
const result = await Notification.requestPermission();
if (result === 'denied') {
  console.error('The user explicitly denied the permission request.');
  return;
}
if (result === 'granted') {
  console.info('The user accepted the permission request.');
}
  1. 返回到应用标签页,然后点击订阅推送。您的浏览器或操作系统可能会询问您是否允许该网站向您发送推送通知。点击允许(或您的浏览器/操作系统使用的任何等效短语)。在 Console 中,您应该看到一条消息,指示请求是被接受还是被拒绝。

订阅推送通知

订阅过程涉及与浏览器供应商控制的 Web 服务(称为推送服务)进行交互。获得推送通知订阅信息后,您需要将其发送到服务器,并让服务器将其长期存储在数据库中。有关订阅过程的更多上下文,请参阅让客户端订阅推送通知

  1. 将以下突出显示的代码添加到 subscribeButtonHandler()
subscribeButton.disabled = true;
const result = await Notification.requestPermission();
if (result === 'denied') {
  console.error('The user explicitly denied the permission request.');
  return;
}
if (result === 'granted') {
  console.info('The user accepted the permission request.');
}
const registration = await navigator.serviceWorker.getRegistration();
const subscribed = await registration.pushManager.getSubscription();
if (subscribed) {
  console.info('User is already subscribed.');
  notifyMeButton.disabled = false;
  unsubscribeButton.disabled = false;
  return;
}
const subscription = await registration.pushManager.subscribe({
  userVisibleOnly: true,
  applicationServerKey: urlB64ToUint8Array(VAPID_PUBLIC_KEY)
});
notifyMeButton.disabled = false;
fetch('/add-subscription', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify(subscription)
});

userVisibleOnly 选项必须为 true。有一天,可能会在不显示用户可见通知(静默推送)的情况下推送消息,但由于隐私问题,浏览器目前不允许这样做。

applicationServerKey 值依赖于一个实用程序函数,该函数将 base64 字符串转换为 Uint8Array。此值用于服务器和推送服务之间的身份验证。

取消订阅推送通知

在用户订阅推送通知后,您的 UI 需要提供一种取消订阅的方式,以防用户改变主意并且不再想接收推送通知。

  1. // TODO 注释替换为 unsubscribeButtonHandler() 中的以下代码:
// TODO
const registration = await navigator.serviceWorker.getRegistration();
const subscription = await registration.pushManager.getSubscription();
fetch('/remove-subscription', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({endpoint: subscription.endpoint})
});
const unsubscribed = await subscription.unsubscribe();
if (unsubscribed) {
  console.info('Successfully unsubscribed from push notifications.');
  unsubscribeButton.disabled = true;
  subscribeButton.disabled = false;
  notifyMeButton.disabled = true;
}

接收推送消息并将其显示为通知

如前所述,您需要 Service Worker 来处理从服务器推送到客户端的消息的接收和显示。有关更多详细信息,请参阅接收推送消息并将其显示为通知

  1. 打开 public/service-worker.js,并将 Service Worker 的 push 事件处理程序中的 // TODO 注释替换为以下代码:
// TODO
let data = event.data.json();
const image = 'https://cdn.glitch.com/614286c9-b4fc-4303-a6a9-a4cef0601b74%2Flogo.png?v=1605150951230';
const options = {
  body: data.options.body,
  icon: image
}
self.registration.showNotification(
  data.title, 
  options
);
  1. 返回到应用标签页。
  2. 点击通知我。您应该收到推送通知。
  3. 尝试在其他浏览器(甚至其他设备)上打开您的应用标签页的 URL,完成订阅工作流程,然后点击通知所有人。您应该在您订阅的所有浏览器上收到相同的推送通知。请回顾浏览器兼容性,查看已知可以工作或无法工作的浏览器/操作系统组合列表。

您可以自定义通知的方式有很多种。请参阅 ServiceWorkerRegistration.showNotification() 的参数以了解更多信息。

当用户点击通知时打开 URL

在现实世界中,您可能会使用通知作为重新吸引用户并提示他们访问您网站的方式。为此,您需要对 Service Worker 进行更多配置。

  1. 将 Service Worker 的 notificationclick 事件处理程序中的 // TODO 注释替换为以下代码:
// TODO
event.notification.close();
event.waitUntil(self.clients.openWindow('https://webdev.ac.cn'));
  1. 返回到应用标签页,向自己发送另一个通知,然后点击该通知。您的浏览器应打开一个新标签页并加载 https://webdev.ac.cn

后续步骤