添加推送通知
发布于
目标: 即使应用不在前台,服务器也能把消息送达用户。据 MDN,Push API 让 Web 应用能够接收 服务器推送来的消息,无论该 Web 应用是否处于前台,甚至是否已被加载;据 MDN,Service Worker 会在需要时被启动以处理到达的推送消息。
这里涉及两个彼此独立、却常被混淆的 API。推送是传输层:据 MDN,它把消息投递到 worker 的
push 事件。通知是展示层:据 MDN,worker 对消息的响应方式之一,就是用
ServiceWorkerRegistration.showNotification() 显示一条通知。
浏览器与生态支持
Section titled “浏览器与生态支持”据 MDN,Push API 属于 Baseline「广泛可用」——在大量设备与浏览器版本上已经成熟,并自 2023 年
3 月起在各浏览器中可用。据 MDN,PushManager.subscribe() 在部分或全部支持它的浏览器中仅在
安全上下文(HTTPS)可用。较弱的一环是 Notification.requestPermission():据 MDN,该静态方法
不属于 Baseline,因为它在一些使用最广泛的浏览器中并不工作。
-
先有一个激活的 Service Worker。 据 MDN,应用要接收推送消息,必须拥有一个激活的 Service Worker;激活之后,它才能通过
PushManager.subscribe()订阅。若还没有,请先看 入门。 -
在用户手势中请求权限。 据 MDN,
Notification.requestPermission()返回一个 Promise, 其结果为granted、denied或default,并且该请求应当在用户交互的响应中发起。据 MDN, 当取值为default时,用户的决定未知,此时应用会按照权限被拒绝来处理。 -
订阅同样要来自用户手势。 据 MDN,
subscribe()调用应当在用户手势(例如点击按钮)的 响应中完成;这不仅是最佳实践,也是因为浏览器正在明确禁止非用户手势触发的通知,MDN 指出 Firefox 自 72 版起已经这样做。 -
把订阅信息发给你的服务器。 据 MDN,得到的
PushSubscription包含应用发送推送消息所 需的全部信息:一个 endpoint,以及发送数据所需的加密密钥。 -
在 worker 中处理
push并展示内容。 据 MDN,到达的推送消息被投递给push事件处理 函数,它可以通过ServiceWorkerRegistration.showNotification()显示通知来作出响应。
特性检测与回退
Section titled “特性检测与回退”据 MDN,ServiceWorkerRegistration.pushManager 是使用推送消息的入口,并返回对 PushManager
接口的引用——因此在展示订阅界面之前,先检测这个接口:
async function subscribeToPush(applicationServerKey) { if (!('PushManager' in window)) { // 不支持推送:保持订阅界面隐藏,回退到你已有的应用内渠道 // (例如站内收件箱视图,或邮件摘要)。 return null; }
const permission = await Notification.requestPermission(); if (permission !== 'granted') return null;
const registration = await navigator.serviceWorker.ready; return registration.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey, });}据 MDN,applicationServerKey 是一个 Base64 编码的字符串或 ArrayBuffer,内含一个 ECDSA
P-256 公钥,推送服务器用它来认证你的应用服务器;一旦指定,你的应用服务器发出的所有消息都
必须使用 VAPID 认证方案,并带上用对应私钥签名的 JWT。据 MDN,该密钥不是你用来加密数据
的那个 ECDH 密钥。
在 worker 中,把消息变成一条通知:
self.addEventListener('push', (event) => { const body = event.data ? event.data.text() : '你有一条新消息。'; event.waitUntil(self.registration.showNotification('新消息', { body }));});userVisibleOnly: true实际上不可省略。 据 MDN,applicationServerKey在 Chrome、 Edge 等部分浏览器中是必需的,并且当userVisibleOnly未设为true时它们会拒绝该 Promise。因此请按「每条推送都对应一条可见通知」来设计,而不是静默的后台投递。- 把 endpoint 当作机密。 据 MDN,订阅 endpoint 是一个唯一的能力 URL:知道这个 endpoint 就足以向你的应用发送消息,因此必须保密,否则其他应用也能向你的用户推送。
- 订阅时要防 CSRF。 MDN 明确警告:实现
PushManager订阅时,防范应用中的 CSRF/XSRF 问题至关重要。 - 投递量并非到处都无限制。 据 MDN,唤醒 Service Worker 投递推送消息会增加资源消耗,尤其 是电量,而目前尚无标准机制来处理这一点:Firefox 对每个应用允许有限的推送消息配额(每次 访问站点时刷新,且会产生通知的消息不计入该上限),Chrome 则没有限制。
- 订阅会过期、会变化。 据 MDN,当推送订阅已失效或即将失效时(例如推送服务设置了过期
时间)会触发
pushsubscriptionchange事件——所以要把已保存的订阅当作可能失效的东西看待, 并在失效时重新订阅。 - 每个订阅只属于一个 worker。 据 MDN,每个订阅对某个 Service Worker 是唯一的。
- 拒绝是明确的。 据 MDN,
denied表示用户已明确拒绝当前源展示系统通知的权限;MDN 自己的 示例也注明,这种情况下应当保持尊重,不必再去打扰用户。
- Web Push 参考 —— 以参考形式呈现的协议与订阅模型。
- Notifications API 参考 —— 展示层的一半,
包括
showNotification()接受哪些参数。 - 通知权限 —— 权限模型的细节。
- iOS Safari 推送 —— 本站的一篇平台专项条目。
← 返回指南总览。