Service Worker 最令人兴奋的功能之一就是消息推送,它允许你的 Web 应用在后台接收来自服务器的消息,即使用户没有 actively 打开你的网站!想象一下,你的电商网站可以在用户购物车里的商品降价时,第一时间通知用户,或者你的新闻网站可以在有突发新闻时立即推送给用户,这该有多棒!
这篇文章将带你深入了解 Service Worker 消息推送的方方面面,即使你只有一点后端开发经验,也能轻松掌握。
1. 消息推送的流程:一次完整的旅程
要理解 Service Worker 的消息推送,咱们先来看一次完整的消息推送流程,就像一次旅行,从出发到抵达,每一步都清清楚楚:
- 用户订阅推送服务: 用户在你的网站上点击“允许通知”按钮,就像买了一张旅行票。浏览器会向推送服务(例如 Firebase Cloud Messaging (FCM) 或 Apple Push Notification service (APNs))请求一个唯一的“订阅凭证”(Subscription),这个凭证包含了推送消息所需的所有信息,就像你的旅行票上的航班号、座位号等。
- 浏览器将订阅凭证发送到你的服务器: 浏览器拿到订阅凭证后,会通过你的 Web 应用将它发送到你的服务器,就像把旅行票交给旅行社。你的服务器需要安全地存储这个凭证,以便后续向该用户推送消息。
- 服务器发送推送消息: 当你的服务器需要向用户推送消息时,它会使用之前存储的订阅凭证,向推送服务发送一个推送请求,就像旅行社向航空公司发出起飞指令。推送请求中包含了要推送的消息内容,以及一些可选的参数,例如消息标题、图标等。
- 推送服务将消息发送到用户的浏览器: 推送服务收到推送请求后,会根据订阅凭证中的信息,找到用户的浏览器,并将消息发送过去,就像航空公司把飞机飞到目的地。
- 浏览器触发 Service Worker 的
push
事件: 用户的浏览器收到消息后,会唤醒负责处理推送消息的 Service Worker(即使你的网站没有打开!),并触发它的push
事件,就像飞机降落后,机场工作人员开始处理后续事宜。 - Service Worker 处理推送消息: Service Worker 在
push
事件的处理函数中,可以获取到推送消息的内容,并根据你的业务逻辑进行处理,例如显示一条通知、更新应用数据等,就像机场工作人员引导旅客出站、提取行李等。 - 用户与通知交互: 用户看到通知后,可以点击通知,或者执行其他操作,这些操作会触发 Service Worker 的
notificationclick
事件,你可以在这个事件的处理函数中处理用户的交互,例如打开网站的某个页面、发送统计数据等,就像旅客离开机场,开始他们的旅程。
是不是很清晰?整个流程就像一次完整的旅行,每一步都有明确的角色和职责。
2. 订阅消息推送:拿到“旅行票”
要实现消息推送,首先要让用户订阅推送服务。这通常通过以下几个步骤完成:
检查浏览器是否支持推送服务: 不是所有浏览器都支持推送服务,所以我们需要先检查一下:
if ('serviceWorker' in navigator && 'PushManager' in window) { // 浏览器支持 Service Worker 和 Push API } else { // 浏览器不支持 console.warn('Push messaging is not supported.'); }
注册 Service Worker: 如果浏览器支持,我们需要先注册一个 Service Worker:
navigator.serviceWorker.register('/sw.js') // 假设你的 Service Worker 文件名为 sw.js .then(function(registration) { // 注册成功 console.log('Service Worker registered with scope:', registration.scope); }) .catch(function(error) { // 注册失败 console.error('Service Worker registration failed:', error); });
请求推送权限: 注册 Service Worker 后,我们需要向用户请求推送权限:
Notification.requestPermission(function(permission) { if (permission === 'granted') { // 用户允许推送 } else if (permission === 'denied') { // 用户拒绝推送 } else if (permission === 'default'){ // 用户忽略了 } });
订阅推送服务: 如果用户允许推送,我们可以使用
PushManager
接口来订阅推送服务:navigator.serviceWorker.ready.then(function(registration) { return registration.pushManager.subscribe({ userVisibleOnly: true, // 必须设置为 true,表示推送的消息必须对用户可见 applicationServerKey: urlBase64ToUint8Array(YOUR_VAPID_PUBLIC_KEY) // 你的 VAPID 公钥,用于标识你的应用 }); }).then(function(subscription) { // 订阅成功,将 subscription 发送到你的服务器 console.log('Push subscription:', subscription); sendSubscriptionToServer(subscription); }).catch(function(error) { // 订阅失败 console.error('Push subscription failed:', error); });
注意:
userVisibleOnly
必须设置为true
,这是为了防止滥用推送服务,确保推送的消息必须对用户可见。applicationServerKey
是一个 VAPID (Voluntary Application Server Identification) 公钥,用于标识你的应用。你需要生成一对 VAPID 公钥和私钥,公钥用于订阅推送服务,私钥用于签名推送请求。你可以使用一些在线工具或库来生成 VAPID 密钥对。urlBase64ToUint8Array
函数用于将 base64 编码的 VAPID 公钥转换为Uint8Array
类型,这是applicationServerKey
参数要求的格式。这个函数的实现可以参考 MDN 文档。
将订阅凭证发送到服务器: 订阅成功后,你会得到一个
PushSubscription
对象,它包含了推送消息所需的所有信息。你需要将这个对象发送到你的服务器,并安全地存储起来。通常使用fetch发送。function sendSubscriptionToServer(subscription) { fetch('/api/save-subscription', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(subscription) }) .then(response => { if (!response.ok) { throw new Error('Bad status code from server.'); } return response.json(); }) .then(responseData => { if (!(responseData.data && responseData.data.success)) { throw new Error('Bad response from server.'); } }); }
3. 处理推送消息:Service Worker 的舞台
当你的服务器向用户发送推送消息时,用户的浏览器会唤醒 Service Worker,并触发它的 push
事件。你需要在 Service Worker 中监听这个事件,并处理推送消息:
// sw.js
self.addEventListener('push', function(event) {
console.log('Received a push message:', event);
let title = '新消息';
let options = {
body: '你有一条新消息!',
icon: '/images/icon.png',
badge: '/images/badge.png',
data: { // 自定义数据
url: '/'
}
};
if (event.data) {
// 如果推送消息中包含了数据,可以使用这些数据
const data = event.data.json();
title = data.title || title;
options.body = data.body || options.body;
}
event.waitUntil(
self.registration.showNotification(title, options)
);
});
代码解释:
self.addEventListener('push', ...)
:监听push
事件。event.data
:获取推送消息中的数据。你可以使用event.data.text()
、event.data.json()
、event.data.blob()
或event.data.arrayBuffer()
等方法来获取不同格式的数据。self.registration.showNotification(title, options)
:显示一条通知。title
是通知的标题,options
是一个配置对象,可以设置通知的各种属性,例如正文、图标、自定义数据等。event.waitUntil(...)
:非常重要!这个方法告诉浏览器,Service Worker 正在执行一个异步操作,不要过早地终止 Service Worker。你需要将所有异步操作(例如showNotification
)放在event.waitUntil
中。- options.data是自定义数据,可以在推送时由服务器加入,用于客户端收到推送时使用。
4. 处理用户交互:notificationclick
事件
当用户点击通知时,Service Worker 会触发 notificationclick
事件。你可以在这个事件的处理函数中处理用户的交互:
// sw.js
self.addEventListener('notificationclick', function(event) {
console.log('Notification click:', event.notification);
event.notification.close(); // 关闭通知
// 打开一个新的窗口/标签页
event.waitUntil(
clients.openWindow(event.notification.data.url)
);
});
代码解释:
event.notification
:获取被点击的通知对象。event.notification.close()
:关闭通知。clients.openWindow(url)
:打开一个新的窗口或标签页,并导航到指定的 URL。你可以根据event.notification.data
中的自定义数据来决定打开哪个 URL。event.waitUntil(...)
: 确保在Service Worker完成操作之前不会被终止。
5. 保证消息推送的可靠性:一些关键点
消息推送的可靠性非常重要,我们需要采取一些措施来确保消息能够成功送达,并且不会丢失或重复:
使用 HTTPS: Service Worker 只能在 HTTPS 环境下工作,这是为了保证安全。
处理推送失败: 推送服务可能会因为各种原因(例如网络问题、用户设备离线等)导致推送失败。你的服务器需要处理这些失败,并进行重试。通常,推送服务会提供一些机制来处理推送失败,例如指数退避重试、消息过期时间等。
防止消息重复: 如果你的服务器在发送推送请求后没有收到推送服务的响应,它可能会认为推送失败,并再次发送相同的请求。这可能会导致用户收到重复的通知。为了防止这种情况,你可以在推送请求中添加一个唯一的标识符,并在 Service Worker 中检查这个标识符,如果已经处理过相同的标识符,就忽略这条消息。
处理 Service Worker 更新: 当你的 Service Worker 代码更新时,浏览器会安装一个新的 Service Worker,但旧的 Service Worker 可能仍然在运行。这可能会导致一些问题,例如旧的 Service Worker 处理了新版本的推送消息。为了避免这种情况,你可以在新的 Service Worker 的
activate
事件中调用clients.claim()
方法,立即接管所有客户端的控制权。用户体验: 不要滥用推送服务,频繁地向用户发送通知会打扰用户,导致用户关闭通知权限。你应该只在真正有价值的时候才向用户推送消息,并提供清晰的退订选项。
- 只推送重要且紧急的通知
- 允许用户自定义通知偏好
- 提供明确的退订选项
总结:你已经掌握了 Service Worker 消息推送!
通过这篇文章,相信你已经对 Service Worker 消息推送有了深入的了解。从消息推送的流程,到订阅、处理、交互,再到可靠性保障,你已经掌握了所有关键知识点。现在,你可以开始在你的 Web 应用中实现消息推送功能了!
记住,Service Worker 消息推送是一个强大的工具,它可以让你的 Web 应用更加生动、更加有用。但同时,它也是一把双刃剑,你需要谨慎使用,不要滥用,才能真正发挥它的价值。
如果你想了解更多关于 Service Worker 的知识,可以参考以下资源:
祝你在 Service Worker 的世界里探索愉快!