企业H5站点升级PWA (九)
如何在我的 Service Worker 中实现推送通知?
下面给你一套最小可用的 Web Push 集成思路,结合你现有的 sw.js 和 index.html,分成两块:
- 前端页面:注册推送、订阅并把订阅信息发给服务器。
- Service Worker:处理
push事件并弹通知。
注意:真正“推送”是服务器发到浏览器的,你本地只能模拟(DevTools 里的“推送”按钮就是在触发
push事件)。
1. 在 sw.js 里处理 push 通知
先在你现有的 sw.js 末尾增加 push 和通知点击事件处理:
self.addEventListener('push', (event) => {
// 约定服务器发送的数据为 JSON 字符串
let data = {};
if (event.data) {
try {
data = event.data.json();
} catch {
data = { body: event.data.text() };
}
}
const title = data.title || 'DensenApp 通知';
const options = {
body: data.body || '您有一条新消息。',
icon: data.icon || './densen26.jpg',
badge: data.badge || './densen26.jpg',
data: {
url: data.url || './index.html', // 点击通知要打开的地址
...data
}
};
event.waitUntil(
self.registration.showNotification(title, options)
);
});
self.addEventListener('notificationclick', (event) => {
event.notification.close();
const url = event.notification.data?.url || './index.html';
event.waitUntil(
self.clients.matchAll({ type: 'window', includeUncontrolled: true })
.then((clientList) => {
// 如果已有同源窗口,直接聚焦并导航
for (const client of clientList) {
if ('focus' in client) {
client.focus();
if (url && 'navigate' in client) {
client.navigate(url);
}
return;
}
}
// 否则新开一个窗口
if (self.clients.openWindow && url) {
return self.clients.openWindow(url);
}
return null;
})
);
});
这样,收到 push 时就会弹系统通知;点击通知会打开(或切换到)你的页面。
2. 在页面端发起推送订阅
2.1 准备 VAPID 公钥
Web Push 需要 VAPID 公私钥对,一般在服务器(.NET)生成:
- 可以用 ASP.NET Core / 控制台配合
WebPush库生成; - 这里只假定你已经拿到一个 Base64 URL 安全格式的公钥字符串,例如:
BPx...你的VAPID公钥...abc
下面代码里用 const VAPID_PUBLIC_KEY = '...'; 占位。
2.2 在 index.html / 某个 JS 里增加订阅逻辑
建议在你当前 index.html 里已有的 Service Worker 注册逻辑旁边,增加一个“初始化推送”的调用。
把原来的 <script> 末尾稍微整理一下:
<script src="bootstrap.bundle.min.js"></script>
<script src="./js/index.js"></script>
<script>
const VAPID_PUBLIC_KEY = '在这里填你的VAPID公钥';
async function initServiceWorkerAndPush() {
if (!('serviceWorker' in navigator)) {
return;
}
// 注册 SW
const registration = await navigator.serviceWorker.register('./sw.js');
console.log('Service Worker 注册成功:', registration.scope);
// 监听 SW 激活刷新
navigator.serviceWorker.addEventListener('message', (event) => {
if (event.data?.type === 'SW_ACTIVATED') {
console.log('Service Worker 已更新到版本:', event.data.version);
window.location.reload();
}
});
// 申请通知权限
const permission = await Notification.requestPermission();
if (permission !== 'granted') {
console.warn('用户拒绝通知权限');
return;
}
// 订阅 push
const subscription = await registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(VAPID_PUBLIC_KEY)
});
console.log('Push 订阅成功:', subscription);
// TODO: 把 subscription 发送到你的服务器保存起来
// 例如:
// await fetch(API_BASE + '/api/push/subscribe', {
// method: 'POST',
// headers: { 'Content-Type': 'application/json' },
// body: JSON.stringify(subscription)
// });
}
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;
}
window.addEventListener('load', () => {
initServiceWorkerAndPush().catch(err => {
console.error('初始化 Service Worker / Push 失败:', err);
});
});
</script>
</body>
</html>
要点:
Notification.requestPermission()会弹系统权限对话框;registration.pushManager.subscribe(...)创建订阅;subscription需要发给你的服务器保存(包含 endpoint + 密钥)。
3. 服务器端发送 Web Push(简要)
你后端是 .NET,可以用 WebPush.Net 之类的库向浏览器推送:
using WebPush;
var subject = "mailto:[email protected]";
var publicKey = "<与你前端相同的VAPID公钥>";
var privateKey = "<VAPID私钥>";
var client = new WebPushClient();
var vapidDetails = new VapidDetails(subject, publicKey, privateKey);
// subscriptionJson 是你从前端保存的 Subscription 对象 JSON
var subscription = JsonSerializer.Deserialize<PushSubscription>(subscriptionJson);
var payload = new
{
title = "DensenApp 通知",
body = "有新的订单,请及时处理",
url = "https://你的站点地址/orders.html"
};
await client.SendNotificationAsync(
subscription,
JsonSerializer.Serialize(payload),
vapidDetails);
这条消息就会触发前面 sw.js 里的 push 事件,然后弹通知。
4. 开发时如何快速验证
-
本地调试:
- 必须在
https://或http://localhost下访问; - 允许浏览器通知;
- 看 console 中是否有“Push 订阅成功”。
- 必须在
-
用 DevTools 的“推送”按钮模拟:
- 在 Application → Service Workers 面板;
- 输入如
{"title":"测试","body":"来自DevTools的推送"}; - 点击“推送”,应看到通知弹出。
如果你希望,我可以再帮你把服务器端 .NET 发送推送那块按你现在的 WebAPI 结构(看看 WebApp.cs)嵌一段实际的控制器示例。
关联项目
FreeSql QQ群:4336577
BA & Blazor QQ群:795206915
Maui Blazor 中文社区 QQ群:645660665
知识共享许可协议
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名AlexChow(包含链接: https://github.com/densen2014 ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系 。
转载声明
本文来自博客园,作者:周创琳 AlexChow,转载请注明原文链接:https://chuna2.787528.xyz/densen2014/p/19623505
AlexChow
今日头条 | 博客园 | 知乎 | Gitee | GitHub


浙公网安备 33010602011771号