推送消息是一种简单有效的方式,可让您重新吸引用户。在此 Codelab 中,您将学习如何向 Web 应用添加推送通知。
学习内容
- 如何订阅和取消订阅推送消息
- 如何处理收到的推送消息
- 如何显示通知
- 如何响应通知点击
所需条件
- Chrome 52 或更高版本
- Web Server for Chrome 或您选择的自有网络服务器
- 文本编辑器
- 具备 HTML、CSS、JavaScript 和 Chrome 开发者工具方面的基础知识
- 示例代码(请参阅“准备工作”)
下载示例代码
您可以通过以下两种方式获取此 Codelab 的示例代码:
- 克隆 Git 代码库:
git clone https://github.com/GoogleChrome/push-notifications.git
- 下载 ZIP 文件:
如果您将源代码下载为 ZIP 文件,解压缩后会得到一个根文件夹 push-notifications-master。
安装并验证 Web 服务器
尽管您可以随意使用自己的网络服务器,但此 Codelab 旨在与 Web Server for Chrome 应用配合使用。如果您尚未安装该应用,可以通过 Chrome 网上应用店获取:
安装 Web Server for Chrome 应用后,点击书签栏中的应用快捷方式:

在“应用”窗口中,点击“Web Server”图标:

接下来,您将看到以下对话框,该对话框可让您配置本地网络服务器:

点击选择文件夹按钮,然后选择您下载的 push-notifications 文件夹中的 app 文件夹。这样一来,您就可以通过对话框的 Web Server 网址(s)(网络服务器网址)部分中显示的网址处理正在进行的工作。
在选项下,勾选自动显示 index.html 旁边的复选框,如下所示:

然后,将 Web Server: STARTED(网络服务器:已启动)切换开关向左滑动,然后再向右滑动,以停止并重启服务器。

点击 Web Server 网址,在网络浏览器中访问您的网站。您应该会看到如下所示的页面,不过您的版本可能会显示 127.0.0.1:8887 作为地址:

始终更新服务工作线程
在开发过程中,确保您的 Service Worker 始终是最新的并包含最新的更改非常有用。
如需在 Chrome 中设置此功能,请执行以下操作:
- 前往 Push Codelab 标签页。
- 打开开发者工具:在 Windows 和 Linux 上按 Ctrl-Shift-I,在 macOS 上按 Cmd-Option-I。
- 选择应用面板,点击 Service Worker 标签页,然后选中重新加载时更新复选框。选中此复选框后,每次重新加载网页时,系统都会强制更新 Service Worker。

在 app 目录中,您会发现有一个名为 sw.js 的空文件。此文件将是您的服务工作线程。目前,您可以将其留空。您稍后会向其中添加代码。
首先,您需要将此文件注册为服务工作线程。
app/index.html 页面加载scripts/main.js。您可以在此 JavaScript 文件中注册服务工作线程。
将以下代码添加到 scripts/main.js:
if ('serviceWorker' in navigator && 'PushManager' in window) {
console.log('Service Worker and Push are supported');
navigator.serviceWorker.register('sw.js')
.then(function(swReg) {
console.log('Service Worker is registered', swReg);
swRegistration = swReg;
})
.catch(function(error) {
console.error('Service Worker Error', error);
});
} else {
console.warn('Push messaging is not supported');
pushButton.textContent = 'Push Not Supported';
}此代码用于检查您的浏览器是否支持 Service Worker 和推送消息传递。如果支持,代码会注册您的 sw.js 文件。
试试看
通过刷新浏览器中的 Push Codelab 标签页来检查您的更改。
在 Chrome 开发者工具中查看控制台,看看是否有 Service Worker is registered message,如下所示:

获取应用服务器密钥
若要完成此 Codelab,您需要生成应用服务器密钥。您可以在配套网站 web-push-codelab.glitch.me 上执行此操作
您可以在此处生成公钥和私钥对。

将您的公钥复制到 scripts/main.js 中,替换 <Your Public Key> 值:
const applicationServerPublicKey = '<Your Public Key>';重要提示:您绝不应将私钥放在 Web 应用中!
目前,Web 应用的启用 按钮处于停用状态,无法点击。这是因为,最佳实践是默认情况下停用推送按钮,并在您知道浏览器支持推送消息传递后启用该按钮,并且能够检查用户当前是否订阅了消息传递。
您需要在 scripts/main.js 中创建两个函数:
initializeUI,以检查用户当前是否已订阅updateBtn,以启用按钮并根据用户是否订阅来更改文本
将 initializeUI 函数添加到 main.js,如下所示:
function initializeUI() {
// Set the initial subscription value
swRegistration.pushManager.getSubscription()
.then(function(subscription) {
isSubscribed = !(subscription === null);
if (isSubscribed) {
console.log('User IS subscribed.');
} else {
console.log('User is NOT subscribed.');
}
updateBtn();
});
}新方法使用上一步中的 swRegistration,从中获取 pushManager 属性,并对该属性调用 getSubscription()。
pushManager。getSubscription() 会返回一个 Promise,如果存在当前订阅,则该 Promise 会解析为当前订阅。否则,返回 null。这样一来,您就可以检查用户是否已订阅、设置 isSubscribed 的值,然后调用 updateBtn() 来更新按钮。
将 updateBtn() 函数添加到 main.js:
function updateBtn() {
if (isSubscribed) {
pushButton.textContent = 'Disable Push Messaging';
} else {
pushButton.textContent = 'Enable Push Messaging';
}
pushButton.disabled = false;
}此函数会启用按钮,并根据用户是否订阅来更改按钮文字。
最后要做的是,当您的服务工作线程在 main.js 中注册时调用 initializeUI():
navigator.serviceWorker.register('sw.js')
.then(function(swReg) {
console.log('Service Worker is registered', swReg);
swRegistration = swReg;
initializeUI();
})试试看
刷新 Push Codelab 标签页。您应该会看到启用推送消息按钮现在处于启用状态(您可以点击它),并且您应该会在控制台中看到 User is NOT subscribed。

在完成此 Codelab 的其余部分时,您应该会看到按钮文字在每次订阅或取消订阅时发生变化。
目前,您的启用推送消息按钮还不能发挥太大作用。让我们解决这个问题。
在 initializeUI() 函数中,为按钮添加点击监听器:
function initializeUI() {
pushButton.addEventListener('click', function() {
pushButton.disabled = true;
if (isSubscribed) {
// TODO: Unsubscribe user
} else {
subscribeUser();
}
});
// Set the initial subscription value
swRegistration.pushManager.getSubscription()
.then(function(subscription) {
isSubscribed = !(subscription === null);
updateSubscriptionOnServer(subscription);
if (isSubscribed) {
console.log('User IS subscribed.');
} else {
console.log('User is NOT subscribed.');
}
updateBtn();
});
}当用户点击该按钮时,您会停用该按钮,以确保用户不会再次点击该按钮,因为订阅推送消息可能需要一些时间。
然后,如果用户当前未订阅,则调用 subscribeUser()。为此,您需要将以下代码粘贴到 scripts/main.js 中:
function subscribeUser() {
const applicationServerKey = urlB64ToUint8Array(applicationServerPublicKey);
swRegistration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: applicationServerKey
})
.then(function(subscription) {
console.log('User is subscribed.');
updateSubscriptionOnServer(subscription);
isSubscribed = true;
updateBtn();
})
.catch(function(error) {
console.error('Failed to subscribe the user: ', error);
updateBtn();
});
}我们来逐步了解此代码的作用以及如何为用户订阅推送消息。
首先,您需要获取应用服务器的公钥(以 Base64 网址 安全编码),并将其转换为 UInt8Array,因为这是 subscribe() 调用的预期输入。urlB64ToUint8Array() 函数位于 scripts/main.js 的顶部。
转换值后,您可以在服务工作线程的 pushManager 上调用 subscribe() 方法,并传入应用服务器的公钥和值 userVisibleOnly: true。
const applicationServerKey = urlB64ToUint8Array(applicationServerPublicKey);
swRegistration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: applicationServerKey
})userVisibleOnly 参数可确保您在每次发送推送消息时都显示通知。目前,此值为必需值,且必须为 true。
调用 subscribe() 会返回一个 promise,该 promise 将在完成以下步骤后解析:
- 用户已授予显示通知的权限。
- 浏览器已向推送服务发送网络请求,以获取生成
PushSubscription所需的数据。
如果这些步骤成功完成,subscribe() promise 将解析为 PushSubscription。如果用户未授予权限,或者在订阅用户时出现任何问题,Promise 将拒绝并显示错误。这样,您就可以在 Codelab 中获得以下 promise 链:
swRegistration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: applicationServerKey
})
.then(function(subscription) {
console.log('User is subscribed.');
updateSubscriptionOnServer(subscription);
isSubscribed = true;
updateBtn();
})
.catch(function(err) {
console.log('Failed to subscribe the user: ', err);
updateBtn();
});这样一来,您要么获得订阅并视用户为订阅者,要么捕获错误并将其记录到控制台。在这两种情况下,您都会调用 updateBtn() 以确保按钮重新启用并显示相应的文本。
在实际应用中,您会在函数 updateSubscriptionOnServer() 中将订阅数据发送到后端,但在本 Codelab 中,您只需在界面中显示订阅即可。将以下函数添加到 scripts/main.js 中:
function updateSubscriptionOnServer(subscription) {
// TODO: Send subscription to application server
const subscriptionJson = document.querySelector('.js-subscription-json');
const subscriptionDetails =
document.querySelector('.js-subscription-details');
if (subscription) {
subscriptionJson.textContent = JSON.stringify(subscription);
subscriptionDetails.classList.remove('is-invisible');
} else {
subscriptionDetails.classList.add('is-invisible');
}
}试试看
前往 Push Codelab 标签页,刷新页面,然后点击该按钮。您应该会看到类似如下所示的权限提示:

如果您授予该权限,您应该会在控制台中看到 User is subscribed 日志。该按钮的文本将更改为 Disable Push Messaging(停用推送消息),并且您将能够在页面底部以 JSON 数据的形式查看订阅。

您尚未处理的一件事是,如果用户屏蔽权限请求,会发生什么情况。这需要一些独特的考虑,因为如果用户屏蔽了权限,您的 Web 应用将无法再次显示权限提示,也无法让用户订阅。您至少需要停用该按钮,以便用户知道它无法使用。
处理此场景的明显位置是在 updateBtn() 函数中。您只需检查 Notification.permission 值,如下所示:
function updateBtn() {
if (Notification.permission === 'denied') {
pushButton.textContent = 'Push Messaging Blocked';
pushButton.disabled = true;
updateSubscriptionOnServer(null);
return;
}
if (isSubscribed) {
pushButton.textContent = 'Disable Push Messaging';
} else {
pushButton.textContent = 'Enable Push Messaging';
}
pushButton.disabled = false;
}您知道,如果权限为 denied,则用户无法订阅,您也无能为力,因此最好永久停用该按钮。
试试看
由于您已在上一步中为 Web 应用授予权限,因此您需要点击网址栏中圆圈内的 i,并将通知权限更改为使用全局默认设置(询问)。

更改此设置后,刷新页面,然后点击启用推送消息按钮,并在权限对话框中选择阻止。该按钮将处于停用状态,并显示“推送消息已屏蔽”文本。

进行此更改后,您现在可以订阅用户,并处理可能的权限场景。
在学习如何从后端发送推送消息之前,您需要考虑订阅用户收到推送消息时实际会发生什么情况。
当您触发推送消息时,浏览器会收到该推送消息,确定该推送消息是针对哪个服务工作线程的,然后唤醒该服务工作线程并分派推送事件。您需要监听此事件,并显示一条通知作为结果。
将以下代码添加到您的 sw.js 文件中:
self.addEventListener('push', function(event) {
console.log('[Service Worker] Push Received.');
console.log(`[Service Worker] Push had this data: "${event.data.text()}"`);
const title = 'Push Codelab';
const options = {
body: 'Yay it works.',
icon: 'images/icon.png',
badge: 'images/badge.png'
};
event.waitUntil(self.registration.showNotification(title, options));
});我们来逐步了解一下此代码。您可以通过添加事件监听器,在服务工作线程中监听 push 事件:
self.addEventListener('push', ... );(除非您之前使用过 Web Worker,否则 self 可能是一个新概念。在服务工作器文件中,self 引用的是服务工作器本身。)
当收到推送消息时,系统会调用事件监听器,然后您可以通过调用服务工作线程的 registration 属性上的 showNotification() 来创建通知。showNotification() 需要 title;您还可以为其提供 options 对象来设置正文消息、图标和标记。(撰写本文时,徽章仅在 Android 上使用。)
const title = 'Push Codelab';
const options = {
body: 'Yay it works.',
icon: 'images/icon.png',
badge: 'images/badge.png'
};
self.registration.showNotification(title, options);push 事件处理中要介绍的最后一项是 event.waitUntil()。此方法接受一个 promise,以使浏览器能够保持您的服务工作线程处于活动状态并运行,直到传入的 promise 已得到解决。
为了让上面的代码更易于理解,您可以将其改写为如下形式:
const notificationPromise = self.registration.showNotification(title, options);
event.waitUntil(notificationPromise);现在,您已逐步了解了推送事件,接下来我们来测试一下推送事件。
试试看
借助服务工作线程中的推送事件处理功能,您可以触发虚假推送事件,以测试在收到消息时会发生什么情况。
在 Web 应用中,订阅推送消息,并确保在控制台中看到 User IS subscribed。在开发者工具的应用面板中,点击 Service Workers 标签页下的 Push 按钮:

点击 Push 后,您应该会看到类似如下所示的通知:

注意:如果此步骤不起作用,请尝试使用开发者工具“应用”面板中的 Unregister 链接取消注册 Service Worker,等待 Service Worker 停止,然后重新加载页面。
如果您点击其中一个通知,会发现没有任何反应。您可以在 Service Worker 中监听 notificationclick 事件,以处理通知点击。
首先,在 sw.js 中添加 notificationclick 监听器:
self.addEventListener('notificationclick', function(event) {
console.log('[Service Worker] Notification click received.');
event.notification.close();
event.waitUntil(
clients.openWindow('https://developers.google.com/web')
);
});当用户点击通知时,系统会调用 notificationclick 事件监听器。
该代码首先关闭了被点击的通知:
event.notification.close();然后,系统会打开一个新窗口或标签页,加载网址 https://developers.google.com/web。您可以随意更改此设置。
event.waitUntil(
clients.openWindow('https://developers.google.com/web/')
);event.waitUntil() 可确保浏览器不会在新窗口或标签页显示之前终止服务工作线程。
试试看
再次尝试在 DevTools 中触发推送消息,然后点击该通知。现在,您会看到通知关闭,并打开一个新标签页。
您已看到,您的 Web 应用能够使用开发者工具显示通知,并且您已了解如何通过点击关闭通知。下一步是发送实际的推送消息。
正常情况下,这需要从网页向后端发送订阅。然后,后端会通过向订阅中的端点发出 API 调用来触发推送消息。
这不属于此 Codelab 的讨论范围,但您可以使用配套网站 (web-push-codelab.glitch.me) 来触发实际的推送消息。将订阅内容粘贴到网页底部:

然后,将此内容粘贴到配套网站的订阅发送至文本区域中:

在要发送的文本下方,添加您要随推送消息发送的任何字符串。
点击发送推送消息按钮。

然后,您应该会收到一条推送消息。您使用的文本将记录到控制台。

这样,您就可以测试数据的发送和接收,并相应地操纵通知。
配套应用只是一个使用 web-push 库发送消息的节点服务器。建议您查看 GitHub 上的 web-push-libs 组织,了解有哪些库可用于为您发送推送消息。这会处理触发推送消息的许多细节。
您可以点击此处查看配套网站的所有代码。
唯一缺少的功能是让用户退订推送。为此,您需要在 PushSubscription 上调用 unsubscribe()。
回到 scripts/main.js 文件中,将 initializeUI() 中的 pushButton 点击监听器更改为以下内容:
pushButton.addEventListener('click', function() {
pushButton.disabled = true;
if (isSubscribed) {
unsubscribeUser();
} else {
subscribeUser();
}
});请注意,您现在要调用一个新函数 unsubscribeUser()。在此函数中,您将获取当前订阅并对其调用 unsubscribe()。将以下代码添加到 scripts/main.js:
function unsubscribeUser() {
swRegistration.pushManager.getSubscription()
.then(function(subscription) {
if (subscription) {
return subscription.unsubscribe();
}
})
.catch(function(error) {
console.log('Error unsubscribing', error);
})
.then(function() {
updateSubscriptionOnServer(null);
console.log('User is unsubscribed.');
isSubscribed = false;
updateBtn();
});
}我们来了解一下这个函数。
首先,您可以通过调用 getSubscription() 获取当前订阅:
swRegistration.pushManager.getSubscription()如果存在 PushSubscription,则返回一个解析为 PushSubscription 的 promise;否则,返回 null。如果有订阅,您会对其调用 unsubscribe(),这会使 PushSubscription 无效。
swRegistration.pushManager.getSubscription()
.then(function(subscription) {
if (subscription) {
// TODO: Tell application server to delete subscription
return subscription.unsubscribe();
}
})
.catch(function(error) {
console.log('Error unsubscribing', error);
})由于调用 unsubscribe() 可能需要一些时间才能完成,因此它会返回一个 promise。您返回该 promise,以便链中的下一个 then() 等待 unsubscribe() 完成。您还可以添加一个 catch 处理程序,以防调用 unsubscribe() 导致错误。之后,您可以更新界面。
.then(function() {
updateSubscriptionOnServer(null);
console.log('User is unsubscribed.');
isSubscribed = false;
updateBtn();
})试试看
您应该能够在 Web 应用中按 Enable Push Messaging(启用推送消息)或 Disable Push Messaging(停用推送消息),并且日志会显示用户订阅和取消订阅的情况。

恭喜您完成此 Codelab!
此 Codelab 已向您展示了如何开始向 Web 应用添加推送通知。如果您想详细了解 Web 通知的功能,请查看这些文档。
如果您想在网站上部署推送通知,可能需要添加对使用 GCM 的旧版浏览器或不符合标准的浏览器的支持。点击此处可了解详情。