你的App消息推送为什么石沉大海?看Service Worker源码我终于懂了

你的App消息推送为什么石沉大海?看Service Worker源码我终于懂了

大多数开发者对Push通知的理解,停留在"调用API发送消息"的表面。但我们很少深入思考:为什么用户关闭浏览器后还能收到通知?这背后的通信机制到底是什么?为什么你的推送打开率这么低?

这篇文章,我们会从源码级别剖析Service Worker、Push API和Notification API的协作原理,以及大厂(字节、阿里云)推荐的实现思路。

一个让人困惑的现象你是否遇到过这样的情况:

调用了Notification.requestPermission(),用户也点了允许,但推送消息要么没收到,要么收到了没人点看似完整的推送流程,在真实生产环境中就是"失效"同样的代码,字节跳动员工的App推送打开率是你的3倍这不是巧合。问题的根源在于:大多数开发者对Push通知的实现停留在"Copy-Paste代码"阶段,从未理解它在浏览器层面的真实工作机制。

Push通知的真实工作流:看不见的三层架构让我先画出完整的消息流转过程:

代码语言:javascript复制┌─────────────────────────────────────────────────────────────────┐

│ 用户点击"允许通知"的那一刻,发生了什么? │

├─────────────────────────────────────────────────────────────────┤

│ │

│ [浏览器进程] [Service Worker进程] │

│ │ │ │

│ 1. 加载SW.js 1. 独立的后台进程 │

│ │ 2. 持久化存活 │

│ 2. 建立Push订阅 3. 监听push/notification事件 │

│ │ │ │

│ └──→ 获取VAPID密钥 ────────────┘ │

│ │ │

│ ├─→ 生成subscription对象 │

│ │ { │

│ │ endpoint: "https://fcm.google.com/...", │

│ │ keys: { p256dh, auth } │

│ │ } │

│ │ │

│ └─→ 发送到服务器保存 │

│ (关键!没有这一步就无法推送) │

│ │

│ [推送服务器](第三方:Google FCM、Apple APNs等) │

│ │ │

│ └─→ 加密消息 ──→ 推送给用户设备 ──→ 触发SW push事件 │

│ │

└─────────────────────────────────────────────────────────────────┘

这个图揭示了一个90%的开发者忽视的问题:

Push订阅对象必须存储在服务器。 如果没有这一步,再完美的前端代码也无法工作。这正是为什么很多人"按教程做了但还是收不到推送"。

第一层:Service Worker注册 — 隐形的守护进程想象Service Worker就像你手机后台运行的应用。即使你关闭浏览器,它仍然可以收信息。

代码语言:javascript复制// ✅ 标准的注册方式(99%的教程都是这样写)

if ('serviceWorker' in navigator) {

navigator.serviceWorker.register('/sw.js')

.then(registration => {

console.log('Service Worker作用域:', registration.scope);

// 重要:记住这个registration对象,后续的push订阅需要它

})

.catch(error => {

console.error('注册失败:', error);

});

}

但这里有个坑: 上面的代码只是注册,并不能保证SW已经完全启动。如果你立刻去调用pushManager.subscribe(),很可能会报错。

正确做法是这样的:

代码语言:javascript复制// ✅ 确保Service Worker已准备就绪

navigator.serviceWorker.ready.then(registration => {

console.log('SW已准备好,可以安全地进行下一步');

// 在这里进行push订阅操作

});

关键词:**ready不是register**。很多人混淆这两个概念,导致竞态条件问题。

为什么Service Worker这么重要?让我们看看浏览器对SW的内存管理机制:

代码语言:javascript复制浏览器生命周期管理:

[Page Active] [Page Closed] [Browser Closed]

│ │ │

├─ 页面SW活跃 ├─ SW仍在运行 ├─ SW持久化

│ (优先级最高) │ (中等优先级) │ 保存状态

│ │ │

└─ 可监听所有事件 └─ 只监听push/ └─ 系统通知

notification事件 触发SW

这就是为什么只有Service Worker能接收来自推送服务的消息。普通的JavaScript(即使在