前端 PWA:推送通知最佳实践
为什么需要推送通知?
在移动互联网时代,用户注意力越来越分散,如何让用户重新回到你的应用?推送通知是一个非常有效的手段。PWA 的推送通知功能,让网页应用也能像原生应用一样发送通知。
PWA 推送通知的核心优势
基础使用
1. 注册 Service Worker
// 注册 Service Worker
if ('serviceWorker' in navigator) {
window.addEventListener('load', async () => {
try {
const registration = await navigator.serviceWorker.register('/sw.js');
console.log('Service Worker 注册成功:', registration);
} catch (error) {
console.error('Service Worker 注册失败:', error);
}
});
}
2. 请求通知权限
// 请求通知权限
async function requestNotificationPermission() {
const permission = await Notification.requestPermission();
if (permission === 'granted') {
console.log('通知权限已授予');
return true;
} else {
console.log('通知权限被拒绝');
return false;
}
}
// 调用
requestNotificationPermission();
3. 订阅推送服务
// 订阅推送服务
async function subscribeToPush() {
const registration = await navigator.serviceWorker.ready;
try {
const subscription = await registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array('YOUR_PUBLIC_VAPID_KEY')
});
// 将订阅信息发送到服务器
await fetch('/api/push-subscribe', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(subscription)
});
console.log('推送订阅成功');
} catch (error) {
console.error('推送订阅失败:', error);
}
}
// 辅助函数
function urlBase64ToUint8Array(base64String) {
const padding = '='.repeat((4 – base64String.length % 4) % 4);
const base64 = (base64String + padding)
.replace(/-/g, '+')
.replace(/_/g, '/');
const rawData = window.atob(base64);
const outputArray = new Uint8Array(rawData.length);
for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i);
}
return outputArray;
}
4. 处理推送事件
// sw.js
self.addEventListener('push', (event) => {
const data = event.data.json();
const options = {
body: data.body,
icon: '/icon.png',
badge: '/badge.png',
vibrate: [100, 50, 100],
data: {
url: data.url
},
actions: [
{
action: 'explore',
title: '查看详情'
},
{
action: 'close',
title: '关闭'
}
]
};
event.waitUntil(
self.registration.showNotification(data.title, options)
);
});
// 处理通知点击
self.addEventListener('notificationclick', (event) => {
event.notification.close();
if (event.action === 'explore') {
event.waitUntil(
clients.openWindow(event.notification.data.url)
);
}
});
最佳实践
1. 通知设计
- 简洁明了:通知内容要简洁,避免冗长
- 个性化:根据用户行为发送相关通知
- 时机恰当:避免在不合适的时间发送通知
- 频率控制:不要发送过于频繁的通知
2. 权限管理
- 合理请求:在用户有足够上下文时请求权限
- 解释用途:向用户解释为什么需要通知权限
- 尊重选择:如果用户拒绝权限,不要反复请求
- 提供设置:允许用户在应用内管理通知设置
3. 后端实现
// 后端 Node.js 示例
const webpush = require('web-push');
// 设置 VAPID 密钥
webpush.setVapidDetails(
'mailto:your-email@example.com',
process.env.VAPID_PUBLIC_KEY,
process.env.VAPID_PRIVATE_KEY
);
// 发送推送通知
async function sendPushNotification(subscription, data) {
try {
await webpush.sendNotification(subscription, JSON.stringify(data));
console.log('推送通知发送成功');
} catch (error) {
console.error('推送通知发送失败:', error);
// 处理订阅过期等情况
if (error.statusCode === 410) {
// 订阅已过期,需要删除
await deleteSubscription(subscription.endpoint);
}
}
}
4. 安全考虑
- 使用 HTTPS:推送通知要求使用 HTTPS
- VAPID 密钥:使用 VAPID 密钥进行身份验证
- 数据加密:推送数据会被加密传输
- 权限控制:只向授权用户发送通知
性能优化
1. 推送频率
- 批量发送:合并多个通知,减少推送次数
- 智能推送:根据用户活跃度调整推送频率
- 时区考虑:根据用户所在时区发送通知
2. 通知内容
- 压缩数据:减少推送数据大小
- 延迟加载:只发送必要信息,详情通过点击加载
- 离线支持:确保通知在离线时也能正常显示
3. 资源管理
- 图标优化:使用合适大小的图标
- 缓存策略:缓存通知所需的资源
- Service Worker 优化:减少 Service Worker 的体积
常见问题与解决方案
1. 通知不显示
原因:权限未授予,或 Service Worker 未正确注册
解决方案:
- 检查通知权限状态
- 确保 Service Worker 正确注册
- 检查浏览器兼容性
2. 推送失败
原因:订阅已过期,或网络问题
解决方案:
- 处理 410 状态码,删除过期订阅
- 实现重试机制
- 监控推送失败率
3. 通知点击无响应
原因:notificationclick 事件处理不当
解决方案:
- 确保在 Service Worker 中正确处理 notificationclick 事件
- 使用 event.waitUntil 确保异步操作完成
- 检查 clients.openWindow 的使用方式
总结
PWA 推送通知是一种强大的用户互动方式,它可以帮助你重新吸引用户,提高用户留存率。通过遵循最佳实践,你可以构建出高效、可靠的推送通知系统。
记住:好的推送通知应该是有价值的、及时的、个性化的。






