navigator.serviceWorker.ready 不触发的主因是环境不满足:PWA 要求 HTTPS(localhost 除外),HTTP 站点即使注册成功也会 pending;需确保页面通过 https:// 访问、SW 脚本路径正确且 MIME 类型为 text/javascript,并尽早执行注册脚本。

为什么 navigator.serviceWorker.ready 一直不触发?
多数人卡在这一步:注册完 Service Worker,却等不到 navigator.serviceWorker.ready Promise resolve。根本原因不是代码写错,而是环境不满足——PWA 推送通知强制要求 HTTPS(本地 localhost 除外),且页面必须通过 https:// 访问。HTTP 站点即使注册成功,serviceWorker.ready 也会 pending,后续所有推送逻辑全失效。
实操建议:
立即学习“前端免费学习笔记(深入)”;
- 开发时用
localhost或127.0.0.1(无需 HTTPS);上线前务必部署 HTTPS,自签名证书不行,得是受信任 CA 签发的 - 确保注册脚本在页面加载后尽早执行,不要包裹在
window.onload或异步模块里,避免竞态 - 检查浏览器控制台是否有
Failed to register a ServiceWorker错误,常见于 SW 脚本路径 404 或 MIME 类型错误(需返回text/javascript)
如何正确调用 registration.pushManager.subscribe()?
这是获取 PushSubscription 的关键步骤,但直接调用会失败——必须先获得用户授权。Chrome、Edge 等现代浏览器已禁用自动弹窗授权,必须由用户显式触发(如点击按钮),否则抛出 DOMException: Permission denied。
实操建议:
立即学习“前端免费学习笔记(深入)”;
- 授权必须走
Notification.requestPermission(),且只在用户交互回调中调用,例如:button.addEventListener('click', async () => { const permission = await Notification.requestPermission(); if (permission === 'granted') { const subscription = await registration.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: urlBase64ToUint8Array('your-vapid-public-key') }); } }); -
userVisibleOnly: true是硬性要求,不加会报错;applicationServerKey必须是 Uint8Array,不能直接传 Base64 字符串,需用工具函数转换(可用crypto.subtle.importKey或现成的urlBase64ToUint8Array) - 订阅成功后,
subscription.endpoint是发送推送的唯一地址,需安全上传至服务端,别存前端 localStorage
后端发推送时为什么收到 400 Bad Request 或 410 Gone?
前端订阅成功 ≠ 后端能顺利发消息。常见问题集中在 VAPID 头和加密格式上。Web Push 协议要求每个请求带 Authorization 和 Encryption 头,缺一不可,且密钥对、盐值、公钥必须严格匹配前端订阅时用的那套。
实操建议:
立即学习“前端免费学习笔记(深入)”;
- VAPID 公钥/私钥必须成对生成,推荐用 Node.js 的
web-push库生成:const webpush = require('web-push'); webpush.generateVAPIDKeys(); // 返回 { publicKey, privateKey }前端用publicKey,后端用privateKey - 发送时若用
web-push.sendNotification(subscription, payload),注意payload长度限制:Chrome 要求加密后 ≤ 4096 字节,空载或小文本最稳;大内容建议只推通知摘要,点击再拉数据 -
410 Gone表示 endpoint 已失效(用户取消订阅、换设备、清缓存),服务端需监听响应状态,及时从数据库删除该订阅
Chrome 上收不到通知,但 DevTools 显示 PushEvent 已触发?
Service Worker 收到 PushEvent 并不代表通知一定弹出。真正决定是否显示的是 self.registration.showNotification() 调用,而它受限于两个隐形条件:SW 是否处于激活态、以及通知权限是否仍为 granted。
实操建议:
立即学习“前端免费学习笔记(深入)”;
- 在 SW 中监听
push事件时,必须用event.waitUntil()包裹异步操作,否则事件可能被提前终止:self.addEventListener('push', event => { const data = event.data?.json() || {}; event.waitUntil( self.registration.showNotification(data.title || 'New message', { body: data.body, icon: '/icon-192.png', badge: '/badge.png' }) ); }); - 不要假设用户始终允许通知——每次 showNotification 前可检查
self.registration.permission,但更稳妥的做法是只在push事件里无条件调用,失败由系统静默处理 - 测试时关闭 Chrome 的“暂停后台标签页”选项(
chrome://flags/#automatic-tab-discarding),否则 SW 可能被休眠,导致 push 不触发
真正的难点不在代码行数,而在于每个环节都依赖前序状态:HTTPS → SW 注册 → 用户授权 → VAPID 配置 → 后端加密 → SW 激活态 → 浏览器通知策略。漏掉任意一环,都会静默失败,且错误信息极其模糊。调试时优先查 Network 面板看 push 请求响应码,再看 Application → Service Workers 的状态和日志,最后才是代码逻辑。



















