仅当标签页关闭时显示推送通知的功能不生效如何解决?
问题原因
你当前的实现存在4个核心问题,导致逻辑不生效:
- Service Worker是事件驱动的短命进程,浏览器会在空闲时自动回收SW实例,你声明在全局的
allowed变量属于内存临时存储,SW被回收后值会被重置,之前接收的start/stop状态会完全丢失。 sw.js中allowed变量没有设置初始值,初始状态为undefined,如果SW是被推送事件唤醒、此前没有收到过任何页面发来的消息,判断逻辑会完全失效。- 页面中
postMessage的执行时机错误:subscribe是异步函数,Service Worker注册是异步过程,你在window.onload中同步调用navigator.serviceWorker.controller.postMessage时,大概率controller还未初始化完成(值为null),消息根本没有发送到SW。 onbeforeunload事件触发不可靠,页面崩溃、浏览器强制杀进程等场景下不会触发,会导致状态永久错误。
最优修复方案
不需要手动维护start/stop状态,直接在SW的推送事件中查询当前是否有活跃的页面窗口即可,完全规避状态丢失问题,实现更稳定:
你可以直接把sw.js替换为以下代码,同时删除main.js中所有和postMessage相关的代码即可:
self.addEventListener('push', event => { event.waitUntil( // 查询所有当前受SW控制的窗口客户端 self.clients.matchAll({ type: 'window', includeUncontrolled: false }).then(clientsList => { // 存在可见的活跃窗口时不弹出通知 const hasActiveWindow = clientsList.some(client => client.visibilityState === 'visible'); if (!hasActiveWindow) { const data = event.data.json(); return self.registration.showNotification(data.title, { body: data.body, icon: data.icon }); } }) ); });
原有逻辑的修复方案(不推荐)
如果你坚持要用原有消息通信的方案,需要做以下修改:
- 把
allowed状态存储到IndexedDB或Cache Storage等持久化存储中,不要存在全局变量 - 把
postMessage逻辑挪到Service Worker注册完成的回调中执行,确保消息能送达 - 给
allowed设置默认初始值true,默认允许未打开页面时推送通知
内容的提问来源于stack exchange,提问作者Adi B.
相关产品推荐
相关产品推荐

