引言
在上一節(jié), 介紹了如何使用 Push API 進行服務端消息推送。提到 Push 就不得不說與其聯(lián)系緊密的另一個 API——Notification API。它讓我們可以在“網(wǎng)站外”顯示消息提示:

即使當你切換到其他 Tab,也可以通過提醒交互來快速讓用戶回到你的網(wǎng)站;甚至當用戶離開當前網(wǎng)站,仍然可以收到系統(tǒng)的提醒消息,并且可以通過消息提醒快速打開你的網(wǎng)站

Notification 的功能本身與 Push 并不耦合,你完全可以只使用 Notification API 或者 Push API 來構(gòu)建 Web App的某些功能。因此,本文會先介紹如何使用 Notification API。然后,作為Notification的“黃金搭檔”,本文還會介紹如何組合使用 Push & Notification(消息推送與提醒)
使用Notification API
獲取提醒權(quán)限
在調(diào)用 Notification 相關API之前,需要先使用Notification對象上的靜態(tài)方法Notification.requestPermission()來獲取授權(quán):
// 申請桌面通知權(quán)限
function requestNotificationPermission() {
// 系統(tǒng)不支持桌面通知
if (!window.Notification) {
return Promise.reject('系統(tǒng)不支持桌面通知')
}
return Notification.requestPermission().then(function (permission) {
if (permission === 'granted') {
return Promise.resolve()
}
return Promise.reject('用戶已禁止桌面通知權(quán)限')
})
}
我們創(chuàng)建了一個requestNotificationPermission()方法來統(tǒng)一Notification.requestPermission()的調(diào)用形式,并在 Service Worker 注冊完成后調(diào)用該方法。調(diào)用Notification.requestPermission()獲取的permissionResult可能的值為:
- denied:用戶拒絕了通知的顯示
- granted:用戶允許了通知的顯示
- default:因為不知道用戶的選擇,所以瀏覽器的行為與denied時相同
chrome 中,可以在 chrome://settings/content/notifications 里進行通知的設置與管理
設置提醒內(nèi)容
獲取用戶授權(quán)后,我們就可以通過registration.showNotification()方法進行消息提醒了
當我們注冊完 Service Worker 后,then方法的回調(diào)函數(shù)會接收一個registration參數(shù),通過調(diào)用其上的showNotification()方法即可觸發(fā)提醒:
// 監(jiān)聽 push 事件
self.addEventListener('push', function (e) {
const { data } = e
if (!data) return
// 解析獲取推送消息
let payload = data.json()
// 根據(jù)推送消息生成桌面通知并展現(xiàn)出來
let title = payload.title
let options = {
body: payload.body || '新消息',
icon: payload.icon || '/img/icons/book-128.png',
data: {
url: payload.url,
},
actions: [
{
action: 'browse',
title: '去看看',
},
{
action: 'contact-me',
title: '聯(lián)系我',
},
],
tag: 'pwa-starter',
renotify: true,
}
let promise = self.registration.showNotification(title, options)
e.waitUntil(promise)
})
title用來設置該提醒的主標題,option 中則包含了一些其他設置
- body:提醒的內(nèi)容
- icon:提醒的圖標
- actions:提醒可以包含一些自定義操作
- tag:相當于是ID,通過該ID標識可以操作特定的 notification
- renotify:是否允許重復提醒,默認為 false。當不允許重復提醒時,同一個 tag 的 notification 只會顯示一次

注: 由于不同瀏覽器中,對于 option 屬性的支持情況并不相同。部分屬性在一些瀏覽器中并不支持
捕獲用戶的點擊
在上一部分中,我們已經(jīng)為 Web App 添加了提醒功能。然而更多的時候,我們并不僅僅希望只展示有限的信息,更希望能引導用戶進行交互。例如推薦一本新書,讓用戶點擊閱讀或購買。
在上一部分我們設置的提醒框中,包含了“去看看”和“聯(lián)系我”兩個按鈕選項,那么怎么做才能捕獲用戶的點擊操作,并且知道用戶點擊了哪個呢?
還記的上一部分里我們定義的 actions 么?
[
{
action: 'browse',
title: '去看看',
},
{
action: 'contact-me',
title: '聯(lián)系我',
},
]
為了能夠響應用戶對于提醒框的點擊事件,我們需要在 Service Worker 中監(jiān)聽notificationclick事件。在該事件的回調(diào)函數(shù)中我們可以獲取點擊的相關信息:
// 監(jiān)聽通知點擊事件
self.addEventListener('notificationclick', function (e) {
console.log('用戶點擊: ', e.action)
})
e.action就是我們在showNotification()中定義的 actions 里的 action 屬性, 可用它來判斷用戶點擊的是什么
注: 當用戶點擊提醒本身時,也會觸發(fā)
notificationclick,但是不包含任何 action 值,所以在代碼中將其置于 default 默認操作中

消息推送與提醒
到目前為止,我們已經(jīng)可以順利得給用戶展示提醒,并且在用戶操作提醒后準確捕獲到用戶的操作。然而,還缺最重要的一步——針對不同的操作,觸發(fā)不同的交互。例如:
- 點擊“看一看”會跳轉(zhuǎn)到對應頁面
- 點擊“聯(lián)系我”會向應用管理者發(fā)郵件等等
// 監(jiān)聽通知點擊事件
self.addEventListener('notificationclick', function (e) {
const { notification = {}, action } = e
const { data = {} } = notification
const { url = '' } = data
console.log('用戶點擊: ', action)
// 關閉窗口
e.notification.close()
// 打開網(wǎng)頁
if (action === 'contact-me') {
e.waitUntil(clients.openWindow('mailto:shenxh0928@gmail.com'))
} else {
e.waitUntil(
// 獲取所有clients
self.clients.matchAll().then(function (clientsList) {
// 切換到該站點的tab
clientsList &&
clientsList.length &&
clientsList[0].focus &&
clientsList[0].focus()
self.clients.openWindow(url)
}),
)
}
})
當用戶點擊提醒后,我們通過notificationclick監(jiān)聽,使用e.waitUntil(clients.openWindow(url))實現(xiàn)不同的操作
至此,一個比較簡單的消息提醒(Notification)功能就完成了
兼容性

目前移動端瀏覽器普遍還不支持該特性,但是在 Mac OS 上的 safari 里面是支持該特性的,不過其調(diào)用方式與上文代碼有些不太一樣。在 safari 中使用 Web Notification 不是調(diào)用registration.showNotification()方法,而是需要創(chuàng)建一個 Notification 對象。
// 監(jiān)聽 push 事件
self.addEventListener('push', function (e) {
const { data } = e
if (!data) return
// 解析獲取推送消息
let payload = data.json()
// 根據(jù)推送消息生成桌面通知并展現(xiàn)出來
let title = payload.title
let options = {
body: payload.body || '新消息',
icon: payload.icon || '/img/icons/book-128.png',
data: {
url: payload.url,
},
actions: [
{
action: 'browse',
title: '去看看',
},
{
action: 'contact-me',
title: '聯(lián)系我',
},
],
tag: 'pwa-starter',
renotify: true,
}
let notification = new Notification(title, options)
notification.addEventListener('click', function (e) {
console.log(e)
})
})
本章分支: notification