深入了解声明式 Web Push
Learn more about Declarative Web Push
2025年6月9日
一句话判断
如果你的 Web Push Service Worker 代码 90% 的逻辑只是把 push 消息翻译成 showNotification() 调用,那声明式 Web Push 让你可以直接删掉整个 Service Worker——浏览器会自动处理剩下的事。
这场 Session 讲了什么
原生 app 从来不需要在设备上写代码来展示 push 通知——通知内容直接在 push 消息中描述,系统自动展示。Web Push 却一直需要一个 Service Worker 来解析消息并调用 showNotification()。这带来了三个问题:你需要写和维护 JavaScript 代码;运行代码消耗 CPU 和电量;ITP(Intelligent Tracking Prevention)会限制 Service Worker JavaScript 的生命周期以保护隐私。
Declarative Web Push 的核心思路很简单:如果大部分网站的 push 消息都是标准 JSON、Service Worker 逻辑都是”解析 JSON 调用 showNotification”,那就让浏览器直接做这件事。在 push 消息中加入一个特殊的 web_push: "8-0-3-0" 键值对,浏览器就会自动解析 JSON 并展示通知,无需启动 Service Worker。
它完全向后兼容:如果浏览器不支持声明式 push(没有找到 magic key),自动回退到原生 Web Push 流程。如果需要更复杂的逻辑(比如解密端到端加密消息),可以设置 mutable: true 让 Service Worker 做可选的后处理。
值得深挖的点
为什么是 “8-0-3-0”
这个 magic key 值来自 IETF RFC 标准编号 8030(RFC 8030 是 Web Push 协议的标准文档)。选择这个值的原因很实际:它极不可能出现在任何已有的 push 消息 JSON 中,所以不会和现有的 Web Push 实现冲突。这是一个巧妙的向后兼容设计——用一个现实中不会出现的值作为”协议升级”的信号。
整个 fallback 链路设计得很优雅:先尝试解析 JSON → 失败则回退到 Service Worker → 成功但没有 magic key 也回退到 Service Worker → 有 magic key 但通知描述无效则静默丢弃 → 有 magic key 且通知有效则自动展示。这意味着即使你的 JSON 格式稍有错误,也不会丢失消息——它会 fallback 到 Service Worker 处理(如果你有的话)。
mutable 模式与 E2E 加密的通知
mutable: true 的设计解决了端到端加密消息的推送场景。比如一个加密聊天应用,服务器不能在 push 消息中包含明文消息内容,只能发送加密数据。设置 mutable: true 后,浏览器会先根据 JSON 中的基本信息(如标题)展示一个占位通知,然后启动 Service Worker 做解密处理。如果 Service Worker 成功解密并提供了替换通知,就展示解密后的内容;如果解密失败(比如密钥不存在),仍然展示原始的占位通知。
这个 fallback 逻辑比原生 Web Push 更可靠——在原生模式下,如果 Service Worker 无法启动(比如被系统回收),用户可能根本收不到通知。声明式模式下,至少有一个基本通知保证展示。这和 iOS/macOS 上 UserNotifications framework 的 mutable-content 行为完全一致。
代码片段
声明式 push 消息的最小有效格式:
{
"web_push": "8-0-3-0",
"notification": {
"title": "新消息",
"navigateURL": "https://app.example.com/messages"
}
}
带完整选项的声明式 push 消息:
{
"web_push": "8-0-3-0",
"notification": {
"title": "浏览器宠物",
"body": "你的仓鼠饿了!",
"navigateURL": "https://app.example.com/pets",
"options": {
"tag": "pet-alert",
"sound": "default"
}
},
"app_badge": 3
}
获取 push subscription 的代码(声明式模式不再需要 Service Worker):
// 无需先注册 Service Worker
const subscription = await window.PushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: vapidPublicKey
});
// 把 subscription 发送到服务器
最佳实践
建议已经使用 Web Push 的网站尽快迁移到声明式格式。迁移成本很低:把现有的 ad-hoc JSON 重构为标准 NotificationOptions 格式,加上 magic key,把 Service Worker 中的 push event handler 简化为直接调用 showNotification(event.data.notification)。
优先保持 Service Worker 作为 fallback——即使迁移到声明式 push,保留一个简化的 Service Worker 可以确保旧浏览器仍然能正常工作。声明式 push 是渐进增强,不是硬切换。
避免在 mutable 模式中执行耗时操作。Service Worker 后处理的目的是解密或微调通知内容,不是做复杂业务逻辑。如果 Service Worker 处理时间过长,系统会超时并回退到原始通知。
还有什么值得关注
- 声明式 push 在 Safari 18.5+ (macOS) 和 iOS 18.4+ (保存到主屏幕的 web app) 上可用。
- 支持 app badge 更新,push 消息中可以直接设置未读计数。
- 从旧的 Safari Push Notification 格式迁移到声明式 push 更容易了,因为两者都是”声明式”的思路。