网页应用想在用户切走标签页之后仍然提醒他们有新消息,最直接的办法就是调用系统级的通知,也就是常说的桌面通知。JavaScript为此提供了一套原生的接口,叫Notification API。它不需要引入任何第三方库,现代浏览器基本都支持,用起来并不复杂,但有几个前置条件和容易踩的坑需要搞清楚,否则代码写完了通知就是弹不出来。

调用系统通知的前置条件
Notification API有一个硬性规定:页面必须运行在安全上下文中,也就是HTTPS协议下,或者本地开发时使用localhost、127.0.0.1。如果你用普通的http协议打开页面,浏览器会直接把Notification标记为不可用,调用时控制台会报错。这一点是新手最容易忽略的,明明代码一模一样,本地能弹线上弹不出来,八成就是协议的问题。
第二个条件是用户授权。系统通知属于敏感权限,浏览器不会允许网页直接弹出,必须先向用户申请。权限有三种状态:granted表示已授权,denied表示被拒绝,default表示用户还没做出选择。可以通过Notification.permission这个静态属性随时查询当前状态。需要注意的是,一旦用户点了拒绝,后续的申请请求会被静默忽略,而且网页自身没有任何办法重置这个状态,只能引导用户去浏览器地址栏左侧的站点设置里手动改回来。
另外提醒一点,申请权限的操作最好绑定在用户的主动行为上,比如点击某个按钮。如果在页面一加载就弹出授权请求,用户体验很差,部分浏览器也会限制这种做法。
从申请权限到弹出通知的完整代码
整个流程分两步:先请求权限,拿到授权后再创建通知对象。请求权限用Notification.requestPermission()方法,它返回一个Promise,这也是目前推荐的写法,旧的回调式写法虽然还能用,但已经不鼓励了。
// 第一步:检查并申请通知权限
function requestNotifyPermission() {
if (!('Notification' in window)) {
console.log('当前浏览器不支持系统通知');
return;
}
if (Notification.permission === 'granted') {
showNotification('权限已就绪', '可以直接发送通知了');
return;
}
if (Notification.permission === 'denied') {
console.log('用户已拒绝授权,请到浏览器设置中手动开启');
return;
}
// default 状态下发起申请
Notification.requestPermission().then(function (permission) {
if (permission === 'granted') {
showNotification('授权成功', '感谢开启通知功能');
}
});
}
// 第二步:创建并显示系统通知
function showNotification(title, body) {
var notification = new Notification(title, {
body: body, // 通知正文
icon: 'https://ipipp.com/icon.png', // 通知图标
tag: 'order-msg', // 相同tag的通知会覆盖旧通知
requireInteraction: true // 通知不自动消失,需用户手动关闭
});
notification.onclick = function () {
window.focus(); // 点击通知时把窗口切到前台
this.close(); // 关闭通知
};
notification.onshow = function () {
console.log('通知已显示');
};
notification.onerror = function () {
console.log('通知出错了');
};
}构造函数的第一个参数是通知标题,必填。第二个参数是一个配置对象,body是通知的正文内容,icon是左侧小图标,tag用来给通知打标签,具有相同tag的新通知会替换掉旧通知而不是堆在一起,这在消息类应用里特别有用,避免通知栏被刷屏。事件回调方面,最常用的是onclick,用户点击通知时触发,典型的处理是聚焦窗口并关闭通知本身。
通知不弹出的常见原因排查
实际开发中经常遇到代码没报错但通知就是不出现的情况,可以按下面几个方向逐项检查。第一,确认操作系统层面有没有关闭浏览器的通知权限。Windows在设置的通知列表里可以单独禁用某个应用,macOS也有类似机制,如果系统层面关了,网页这边权限是granted也没用。
第二,检查浏览器的免打扰或勿扰模式。比如macOS开启了勿扰模式,所有通知都会被压制。第三,注意移动端浏览器的差异,iOS的Safari早期完全不支持网页Notification,后来虽然放开了,但要求通知必须通过安装到主屏的PWA配合Service Worker来发送,直接在页面上new Notification是行不通的,这是兼容性上最大的一个坑。
第四,后台标签页的限流问题。Chrome对后台标签页有节流策略,如果页面完全被挂起,定时器可能不准确,导致该发通知的时机发不出来。针对这种情况,更稳妥的方案是把通知逻辑放到Service Worker里,用registration.showNotification()来发送,这也是实现Web Push推送的标准路径。Service Worker不依赖页面是否打开,即使页面已经关闭,只要浏览器进程在,通知照样能送达。
// 通过 Service Worker 发送通知(适用于PWA和Web Push)
navigator.serviceWorker.register('sw.js').then(function (registration) {
registration.showNotification('新订单提醒', {
body: '您有一笔新订单待处理',
icon: 'https://ipipp.com/icon.png',
tag: 'new-order',
data: { url: 'https://ipipp.com/orders' }
});
});
// 在 sw.js 中处理通知点击
self.addEventListener('notificationclick', function (event) {
event.notification.close();
event.waitUntil(
clients.openWindow(event.notification.data.url)
);
});封装一个可直接复用的通知工具函数
如果项目里多处需要发通知,建议简单封装一下,把权限检查、降级提示这些逻辑收敛到一个函数里。下面的实现考虑了几种边界情况:不支持Notification时降级为页面内的提示,权限被拒时给出友好引导,正常情况下直接发送通知。
function sendNotification(options) {
var title = options.title || '系统提醒';
var opt = {
body: options.body || '',
icon: options.icon || '/favicon.ico',
tag: options.tag || Date.now() + '',
onclick: options.onClick || null
};
if (!('Notification' in window)) {
fallbackTip(opt.body);
return;
}
if (Notification.permission === 'granted') {
fire(title, opt);
} else if (Notification.permission !== 'denied') {
Notification.requestPermission().then(function (p) {
if (p === 'granted') fire(title, opt);
else fallbackTip(opt.body);
});
} else {
fallbackTip(opt.body);
}
function fire(t, o) {
var n = new Notification(t, o);
if (o.onclick) n.onclick = o.onclick;
}
function fallbackTip(text) {
console.warn('通知不可用,降级为页面提示:', text);
}
}
// 使用示例
sendNotification({
title: '任务完成',
body: '您的报表已生成完毕',
tag: 'report-done',
onClick: function () { window.focus(); }
});封装的核心思路是保证调用方无感知:不管权限处于什么状态,函数内部自己消化,失败时降级到控制台或页面内的提示条。对于消息类系统,还可以进一步封装队列,把授权前积累的消息在授权成功后统一补发,体验会更完整。总的原则是,系统通知只是提醒手段的一种,永远要准备好它被拒绝或不可用时的替代方案,不能让关键信息只依赖这一条通道传达。
js系统通知Notification API浏览器桌面通知修改时间:2026-09-12 23:32:39