跳转到内容

通知权限模型

发布于 更新于

一句话: Notification.permission 会报告三种状态之一——default、granted 或 denied——根据 MDN 的说明,代码只应在真实的用户手势响应中调用 Notification.requestPermission(),绝不应在页面加载时调用。

根据 MDN 的 Notification.permission 参考文档:

  • default —— 用户尚未做出选择;在用户授予权限之前,浏览器会将其当作 denied 处理。
  • granted —— 用户已同意;在桌面端,页面可以使用 Notification 构造函数 构造通知(根据 MDN 的说明,大多数移动端浏览器会抛出异常,需改用 ServiceWorkerRegistration.showNotification())。
  • denied —— 用户已拒绝;通知将不会显示。

根据 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 就无法再展示通知。
}
}

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
  1. 仅限已添加到主屏幕的 Web 应用;普通浏览器标签页不可用。
  2. 依据 MDN browser-compat-data,不支持。

源数据: /compatibility/notification-permission.json · 全球使用占比: 67 % (StatCounter 2026-05)

来源: 规范 · MDN · 最近核验 2026-09-08 · 置信度: 中 (由来源计算)

各浏览器对相关 API 的具体支持情况参见 通知 API 与 Web Push。

  • 只在用户手势的事件处理函数中调用 requestPermission(),绝不在页面加载时调用。
  • 将 default 当作“尚未获得许可”处理——不要假定请求一定会成功。
  • 一旦 Notification.permission 变为 denied,就不要再按计划反复调用 requestPermission() 指望得到不同的答案。
  • 在页面代码中使用该 API 前先做特性检测 'Notification' in window—— 根据 MDN 的说明,Notification.permission 在没有 window 的 Web Worker 中也可访问。
  • 不要假定推送订阅一定需要激活的 service worker 注册——Push API 还定义了 窗口范围的路径。