计划是这里的关键词——这是一个相当新的功能!当推送通知被计划(例如,“服药”或“您将在 3 小时后起飞”)时,这意味着即使用户离线,也可以向用户显示它。这与过去有所改进,过去推送通知需要用户在线。
那么计划推送通知是如何工作的呢?我们将重点关注四个关键部分
- 注册服务工作线程
- 添加和删除计划推送通知
- 使用操作按钮增强推送通知
- 在服务工作线程中处理推送通知
首先,一些背景知识
推送通知是告知网站用户发生某些重要事件并且他们可能希望再次打开我们的(网络)应用程序的好方法。使用 通知 API——结合 推送 API 和 HTTP Web 推送协议——网络成为了一种从服务器向应用程序发送推送通知并在设备上显示它的简单方法。

您可能已经看到此类功能的发展。例如,您多久会看到某种允许您接受来自网站的通知的警报?虽然浏览器供应商已经在努力寻找解决方案来减少这种烦人的体验(Firefox 和 Chrome 都已概述了计划),Chrome 80 刚刚开始为新的 通知触发器 API 进行源代码试用,它允许我们创建由不同事件触发的通知,而不仅仅是服务器推送。但是,目前,基于时间的触发器是我们拥有的唯一受支持的事件。但是,其他事件,例如基于地理位置的触发器,已经在计划中。
在 JavaScript 中计划事件非常简单,但存在一个问题。在我们的推送通知场景中,我们无法确定应用程序是否在我们想要显示通知的确切时间运行。这意味着我们不能只在应用程序层对其进行计划。相反,我们需要在服务工作线程级别进行。这就是新 API 发挥作用的地方。
通知触发器 API 处于早期反馈阶段。您需要在 Chrome 中启用 #enable-experimental-web-platform-features
标志,或者您应该为源代码试用注册您的应用程序。
此外,服务工作线程 API 需要通过 HTTPS 进行安全连接。因此,如果您在您的机器上尝试它,您需要确保它通过 HTTPS 提供服务。
设置
我创建了一个非常基本的设置。我们有一个 application.js
文件,一个 index.html
文件和一个 service-worker.js
文件,以及一些图像资源。
/project-folder
├── index.html
├── application.js
├── service-worker.js
└── assets
├─ badge.png
└── icon.png
您可以在 GitHub 上找到基本 通知触发器 API 演示 的完整示例。
注册服务工作线程
首先,我们需要注册一个服务工作线程。目前,它除了记录注册成功之外,什么也不做。
// service-worker.js
// listen to the install event
self.addEventListener('install', event => console.log('ServiceWorker installed'));
<!-- index.html -->
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/service-worker.js');
}
</script>
设置推送通知
在我们的应用程序中,我们需要请求用户的权限来显示通知。从那里,我们将获得服务工作线程注册并为该范围注册一个新的通知。到目前为止,没有什么新东西。
有趣的部分是新的 showTrigger
属性。这使我们能够定义显示通知的条件。目前,我们想要添加一个新的 TimestampTrigger
,它接受一个时间戳。并且由于所有事情都直接发生在设备上,因此它也可以在离线状态下工作。
// application.js
document.querySelector('#notification-button').onclick = async () => {
const reg = await navigator.serviceWorker.getRegistration();
Notification.requestPermission().then(permission => {
if (permission !== 'granted') {
alert('you need to allow push notifications');
} else {
const timestamp = new Date().getTime() + 5 * 1000; // now plus 5000ms
reg.showNotification(
'Demo Push Notification',
{
tag: timestamp, // a unique ID
body: 'Hello World', // content of the push notification
showTrigger: new TimestampTrigger(timestamp), // set the time for the push notification
data: {
url: window.location.href, // pass the current url to the notification
},
badge: './assets/badge.png',
icon: './assets/icon.png',
}
);
}
});
};
处理通知
现在,通知应该在指定的时间戳显示。但是现在我们需要一种与之交互的方法,这就是我们需要服务工作线程 notificationclick
和 notificationclose
事件的地方。
这两个事件都侦听相关的交互,并且都可以使用服务工作线程的全部功能。例如,我们可以打开一个新窗口
// service-worker.js
self.addEventListener('notificationclick', event => {
event.waitUntil(self.clients.openWindow('/'));
});
这是一个非常简单的示例。但是凭借服务工作线程的功能,我们可以做更多的事情。让我们检查所需的窗口是否已打开,并且仅在它未打开时才打开一个新窗口。
// service-worker.js
self.addEventListener('notificationclick', event => {
event.waitUntil(self.clients.matchAll().then(clients => {
if (clients.length){ // check if at least one tab is already open
clients[0].focus();
} else {
self.clients.openWindow('/');
}
}));
});
通知操作
另一种促进与用户交互的好方法是在通知中添加预定义的操作。例如,我们可以让他们选择是否要关闭通知或打开应用程序。
// application.js
reg.showNotification(
'Demo Push Notification',
{
tag: timestamp, // a unique ID
body: 'Hello World', // content of the push notification
showTrigger: new TimestampTrigger(timestamp), // set the time for the push notification
data: {
url: window.location.href, // pass the current url to the notification
},
badge: './assets/badge.png',
icon: './assets/icon.png',
actions: [
{
action: 'open',
title: 'Open app’
},
{
action: 'close',
title: 'Close notification',
}
]
}
);
现在我们在服务工作线程中使用这些通知。
// service-worker.js
self.addEventListener('notificationclick', event => {
if (event.action === 'close') {
event.notification.close();
} else {
self.clients.openWindow('/');
}
});
取消推送通知
也可以取消挂起的通知。在这种情况下,我们需要从服务工作线程获取所有挂起的通知,然后在它们发送到设备之前关闭它们。
// application.js
document.querySelector('#notification-cancel').onclick = async () => {
const reg = await navigator.serviceWorker.getRegistration();
const notifications = await reg.getNotifications({
includeTriggered: true
});
notifications.forEach(notification => notification.close());
alert(`${notifications.length} notification(s) cancelled`);
};
通信
最后一步是使用服务工作线程客户端上的 postMessage
方法设置应用程序和服务工作线程之间的通信。假设我们想通知已经激活的选项卡发生了推送通知点击事件。
// service-worker.js
self.addEventListener('notificationclick', event => {
event.waitUntil(self.clients.matchAll().then(clients => {
if(clients.length){ // check if at least one tab is already open
clients[0].focus();
clients[0].postMessage('Push notification clicked!');
} else {
self.clients.openWindow('/');
}
}));
});
// application.js
navigator.serviceWorker.addEventListener('message', event => console.log(event.data));
总结
通知 API 是一项非常强大的功能,可以增强 Web 应用程序的移动体验。由于通知触发器 API 的到来,它得到了非常重要的改进。该 API 仍在开发中,因此现在是尝试它并向 开发人员提供反馈 的最佳时机。
如果您正在使用 Vue 或 React,我建议您查看 我自己的渐进式 Web 应用程序演示。它包括一个使用通知触发器 API 的这两个框架的文档化示例,如下所示

亲爱的 Nico,好文章。我想知道,即使浏览器关闭,通知也会触发。这是因为它被计划了吗?所以浏览器是打开的,通知被计划,并且当计划满足时通知被触发。现在例如,即使用户没有打开浏览器,是否可以远程触发通知?即使浏览器关闭,服务工作线程是否仍在运行?
嗨 Ruud,谢谢!
首先让我们看一下远程触发的通知(通过 HTTP Web 推送从服务器触发)。对于桌面浏览器,浏览器(但不是应用程序)必须正在运行才能接收消息(https://caniuse.cn/#feat=push-api -> 注意 2),但在移动浏览器上,即使浏览器已关闭,它也能工作。通知触发器 API 也适用相同规则,其中触发器来自设备本身。
我们可以在 Android 网页应用中使用它吗?
嗨,Rohit,
是的,它适用于 Android 版 Chrome。因此,如果您想专门针对此浏览器,则可以在您的 Web 应用中使用它。但您应该记住,它仍在开发中,您应该为您的应用注册源代码试用。
此致,Nico
这对营销人员来说是一个巨大的机会,但许多人错过了它。
不错的文章,帮助我很多!
其他几个关于计划通知的来源建议使用以下方法检查可用性:
"showTrigger" in Notification.prototype
.但是,此表达式在某些 Chromium 浏览器(例如 Brave)中也为真,但通知会立即发送。您是否有关于如何检查是否可以使用计划通知的提示?
非常感谢,
Silas
嗨,Silas
奇怪。我刚刚在 Windows 版 Brave 1.9.76 的控制台中测试了它,它返回 false。
您尝试的是哪个版本的 Brave?
此致,
Nico
嗨,
感谢回复。我安装的 Brave 版本也是 1.9.76,Chromium 版本是 81.0.4044.138(正式版本)(64 位)。
我在控制台中、我的 Angular 项目中以及 web.dev/notification-triggers 上链接的演示应用程序中对其进行了测试。结果始终为 true/通知直接显示……
这是一个非常奇怪的行为。但由于此表达式对您有效,我将检查可能存在的问题。也许其他人也遇到了同样的问题。
嗨,Nico,好文章
即使浏览器关闭,是否有办法发送桌面通知?
亲爱的 Alex,您阅读了之前的留言/回复吗?
我看到这个功能后非常兴奋,正准备开始构建一个应用,但后来我发现源代码试用今天就结束了!除了评论/发布/哭泣之外,我们还能做些什么来帮助推动这项工作向前发展?
嗨,Lars,
是的,这很糟糕。但我相信这只是一个暂时的暂停。我认为网络推送通知的主要问题在于浏览器支持。只要 Apple 拒绝实施最基本的功能,Chrome 就没有必要实施越来越多的功能。所以我真的希望 Apple 在不久的将来改变主意。
但在那之前,您仍然可以通过启用实验性 Web 平台功能标志来试用该 API。
您好,这适用于 PWA 吗?
提前感谢 :)
更新
我在 TimestampTrigger 上遇到一个错误。它显示
“TimestampTrigger”未定义。