通知权限模型
发布于 更新于
一句话: Notification.permission 会报告三种状态之一——default、granted
或 denied——根据 MDN 的说明,代码只应在真实的用户手势响应中调用
Notification.requestPermission(),绝不应在页面加载时调用。
根据 MDN 的 Notification.permission 参考文档:
default—— 用户尚未做出选择;在用户授予权限之前,浏览器会将其当作denied处理。granted—— 用户已同意;在桌面端,页面可以使用Notification构造函数 构造通知(根据 MDN 的说明,大多数移动端浏览器会抛出异常,需改用ServiceWorkerRegistration.showNotification())。denied—— 用户已拒绝;通知将不会显示。
从用户手势中请求权限
Section titled “从用户手势中请求权限”根据 MDN 的《使用通知 API》指南,权限请求应在点击等用户手势响应中发出, 浏览器正越来越多地拒绝并非由手势触发的请求——MDN 指出 Firefox 72+ 和 Safari 已经实施了这一限制。应在手势响应中询问,而不是在加载时:
document.getElementById('enable-notifications').addEventListener('click', async () => { if (!('Notification' in window)) { // 兜底:此浏览器完全没有 Notification API。 return; } const permission = await Notification.requestPermission(); if (permission !== 'granted') { // 兜底:用户拒绝(或直接关闭)了提示——不要当作已授权继续执行。 return; } await showEnabledNotification();});
async function showEnabledNotification() { // 根据 MDN 的说明,大多数移动端浏览器虽然暴露了 `Notification`,但其构造 // 函数会抛出 TypeError,需改用 // `ServiceWorkerRegistration.showNotification()`——因此在有已注册的 // service worker 时优先走这条路径,而不是假定构造函数在所有已授权场景 // 下都能用。 const registration = 'serviceWorker' in navigator ? await navigator.serviceWorker.getRegistration() : undefined; if (registration) { await registration.showNotification('通知已开启'); return; } try { new Notification('通知已开启'); } catch { // 兜底:没有已注册的 service worker,且构造函数抛出了异常(大多数 // 移动端浏览器如此)——没有已注册的 service worker 就无法再展示通知。 }}被拒绝后就不要再问
Section titled “被拒绝后就不要再问”MDN 自己在 requestPermission() 示例代码中的注释写道,用户一旦拒绝通知,
“就没有必要再打扰他们了”——不要按计划反复调用 requestPermission() 指望得到
不同的答案。MDN 自己的《使用通知 API》指南则采取了更轻的做法,在用户拒绝后
仍保留启用按钮可见,让用户之后有机会改变主意;无论采用哪种方式,请求本身
都不应自动重复。
推送订阅并不完全依赖 service worker
Section titled “推送订阅并不完全依赖 service worker”根据 Push API 规范,PushManager 在 Window 和 ServiceWorkerRegistration
上都可用:Window 上的 PushManager 关联的 service worker 注册为 null,
而 ServiceWorkerRegistration 上的 PushManager 则绑定到该注册。规范还定义了
可在窗口上访问的推送订阅,并允许声明式推送消息在不经过页面自身脚本的情况下展示通知。
通过 subscribe() 请求推送订阅是独立于上述 Notification 权限的另一套权限流程。
根据 MDN 的浏览器兼容性数据,Notification.permission 与
requestPermission() 属于有限可用(Limited availability)——不属于
Baseline,因为它们在一些广泛使用的浏览器中表现并不一致。桌面端支持较为
广泛(Chrome 32+、Edge 14+、Firefox 22+、Safari 7+),Android 上的
Chrome、Firefox 与 Samsung Internet 对权限属性与 requestPermission()
的支持同样完整。Android 上的 WebView 完全不支持权限相关 API。iOS 上的
Safari 仅对用户已添加到主屏幕的 Web App 开放(16.4+),普通浏览器标签页
无法使用。只在用户手势中请求权限是 MDN 给出的建议、也是部分浏览器已经
强制的行为,而非规范本身的要求。
需要注意的是,另一个独立的 Notification() 构造函数在部分浏览器上有其
自身的 Android 限制,但这不在本兼容性表的覆盖范围内,因为它并不适用于
这里介绍的权限属性与 requestPermission()。
- 图例
- 支持
- 部分支持
- 需开启标志
- 不支持
- 未知
| 浏览器 / 平台 | 支持 | 版本 | 置信度 | 来源 | 备注 |
|---|---|---|---|---|---|
| Chrome (Desktop) | 支持 | 32 | 中 | 来源 | — |
| Chrome (Android) | 支持 | 42 | 中 | 来源 | — |
| Edge (Desktop) | 支持 | 14 | 中 | 来源 | — |
| Firefox (Desktop) | 支持 | 22 | 中 | 来源 | — |
| Firefox (Android) | 支持 | 22 | 中 | 来源 | — |
| Safari (macOS) | 支持 | 7 | 中 | 来源 | — |
| Safari (iOS) | 部分支持 | 16.4 | 中 | 来源 | 1 |
| Samsung Internet | 支持 | 4.0 | 中 | 来源 | — |
| WebView (Android) | 不支持 | — | 中 | 来源 | 2 |
- 仅限已添加到主屏幕的 Web 应用;普通浏览器标签页不可用。
- 依据 MDN browser-compat-data,不支持。
各浏览器对相关 API 的具体支持情况参见 通知 API 与 Web Push。
- 只在用户手势的事件处理函数中调用
requestPermission(),绝不在页面加载时调用。 - 将
default当作“尚未获得许可”处理——不要假定请求一定会成功。 - 一旦
Notification.permission变为denied,就不要再按计划反复调用requestPermission()指望得到不同的答案。 - 在页面代码中使用该 API 前先做特性检测
'Notification' in window—— 根据 MDN 的说明,Notification.permission在没有window的 Web Worker 中也可访问。 - 不要假定推送订阅一定需要激活的 service worker 注册——Push API 还定义了 窗口范围的路径。