“通知权限点了允许,为什么还是收不到推送?”浏览器推送不是单个 API,而是 Permission、Service Worker、PushManager 和推送服务四层配合。任何一层缺失,订阅都不会生效。
Notification.permission 有 default、granted、denied 三个状态。Notification.requestPermission() 必须在用户手势里调用,页面加载时自动弹窗通常会被浏览器拦截。
const permission = await Notification.requestPermission();
console.log(permission);用户拒绝后,页面不能再次主动弹窗,只能引导用户去浏览器设置里修改。不同浏览器对用户手势的要求不同:Safari 更严格,部分版本要求直接点击;Chrome 允许在用户交互后调用。不要在页面加载时自动请求,容易被静默拒绝。
权限状态是 per-origin 的,同一浏览器 profile 下同一域名共享;换账号不会自动重置权限,换 profile 才会。
推送依赖 Service Worker。先注册 SW,等 navigator.serviceWorker.ready,再调用 pushManager.subscribe():
const registration = await navigator.serviceWorker.ready;
const subscription = await registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(VAPID_PUBLIC_KEY)
});
console.log(subscription.endpoint);userVisibleOnly: true 表示每条推送都会展示通知;applicationServerKey 是服务端的 VAPID 公钥,必须和发送端私钥配对。公钥要转成 Uint8Array,不能直接传 base64 字符串。pushManager.getSubscription() 可以先检查已有订阅,避免重复订阅;如果返回 null,再调用 subscribe。
subscription 包含 endpoint、expirationTime 和 keys.p256dh/keys.auth;服务端要保存这些字段,发送时按协议加密 payload。
浏览器把 subscription 的 endpoint 和 keys 发给服务端。服务端用 VAPID 私钥签名,向 endpoint 发 POST;推送服务再把消息转给浏览器,触发 SW 的 push 事件:
self.addEventListener('push', (event) => {
const data = event.data ? event.data.json() : {};
event.waitUntil(self.registration.showNotification(data.title || '通知', {
body: data.body || ''
}));
});没有 showNotification,浏览器可能判定为静默推送并限制。推送请求可以带 TTL 和 Urgency 头;TTL 过期后推送服务会丢弃消息。payload 通常加密,服务端不要假设浏览器能直接读到明文。
推送服务返回 201/202 表示接受,404/410 表示订阅失效,需要删除;429 表示限流,按 Retry-After 退避。服务端可以用 web-push 这类库发送,核心是 VAPID 公私钥和订阅对象;发送失败要按状态码清理订阅。
第一,没有 SW 或 SW 未激活,serviceWorker.ready 一直不返回。
第二,VAPID 公钥格式错误。applicationServerKey 需要 Uint8Array,不是普通字符串。
第三,权限被拒绝。denied 后只能用户手动改,代码无法绕过。
第四,订阅过期。浏览器可能触发 pushsubscriptionchange,需要在事件里重新订阅并同步服务端。
第五,无痕模式/隐私设置。部分浏览器在无痕窗口禁用推送,或关闭窗口后清除订阅。iOS 上的 Web Push 需要把站点添加到主屏幕(PWA)后才可用,Safari 的限制比桌面浏览器多;上线前要按目标设备验证。
pushsubscriptionchange 事件里要重新 subscribe,并把新 endpoint 同步给服务端;否则旧订阅失效后服务端还在往旧地址发。notificationclick 事件里调用 clients.openWindow 或聚焦已有窗口,否则点击通知没有反应。
推送订阅按 origin 和浏览器 profile 存储。同一个 profile 下切换账号,订阅不会自动按账号分开;不同 profile 才会各自独立。清理站点数据、卸载 SW 或更换浏览器都可能导致订阅失效,服务端需要能处理 404/410 并删除旧订阅。
不要反复调用 requestPermission;引导用户点地址栏的权限图标,或到浏览器设置里改。代码无法绕过 denied。Safari 桌面版支持 Web Push,但要求用户手势;iOS/iPadOS 需要把站点添加到主屏幕,且系统版本有要求。面向移动端时,先确认目标设备支持,再设计订阅入口。
Notification.permission 是否为 granted;navigator.serviceWorker.ready 是否返回;pushManager.getSubscription() 是否已有订阅;浏览器推送是四层链路:权限、SW、订阅、推送服务。排查时从 Notification.permission 开始,再看 serviceWorker.ready、pushManager.getSubscription(),最后查服务端 VAPID 和推送响应。只检查“权限已允许”不够。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。