- 原文地址:How JavaScript works: the mechanics of Web Push Notifications
- 原文做者:Alexander Zlatkov
- 譯文出自:掘金翻譯計劃
- 本文永久連接:github.com/xitu/gold-m…
- 譯者:Starrier
- 校對者:allen、老教授
這是專門研究 JavaScript 及其構建組件系列文章的第 9 章。在識別和描述核心元素的過程當中,咱們還分享了咱們在構建一個輕量級 JavaScript 應用程序 SessionStack 時使用的一些經驗規則,該應用程序須要健壯、高性能,能夠幫助用戶實時查看和重現它們的 Web 應用程序缺陷。javascript
若是你錯過了前幾章,你能夠在這裏找到它們:html
今天,咱們來關注 Web 推送通知:咱們將瞭解它們的構建組件,探索發送/接收通知的流程,最後分享 SessionStack 是如何利用這些來構建新產品的特性。前端
推送通知在手機領域被普遍使用。因爲某種緣由,它們很晚才進入 Web 領域,儘管開發人員呼喚了好久。java
Web 推送通知容許用戶在 Web 應用程序中選擇接收更新信息,這些旨在從新吸引用戶羣注意的更新信息一般是對用戶來講有趣、重要、實時的內容。android
推送基於咱們在上一篇文章中詳細討論過的 Service Worker。ios
在這種狀況下,使用 Service Worker 的緣由是它們在後臺工做。這對推送通知很是有用,由於這意味着只有當用戶與通知自己進行交互時纔會執行它們的代碼。git
推送和通知是兩種不一樣的 API。github
實現推送有三個步驟:web
如今咱們將更詳細地描述整個過程。數據庫
首先,咱們須要檢查當前瀏覽器是否支持推送消息。咱們能夠經過兩個簡單的方法檢查是否支持推送消息:
navigator
對象上的 serviceWorker
window
對象上的 PushManager
兩種檢查看起來都是這樣的:
if (!('serviceWorker' in navigator)) {
// Service Worker isn't supported on this browser, disable or hide UI. return; } if (!('PushManager' in window)) { // Push isn't supported on this browser, disable or hide UI.
return;
}
複製代碼
此時,咱們知道該功能是受支持的。下一步是註冊咱們的 Service Worker。
如何註冊 Service Worker,你從咱們之前的一篇文章中應該已經熟悉了。
在註冊了 Service Worker 以後,咱們能夠開始訂閱用戶。要作到這一點,咱們須要獲得他的許可才能給他發送推送信息。
獲取許可的 API 相對簡單,但缺點是 API 已經從接受回調變爲返回 Promise,這帶來了一個問題:咱們沒法判斷當前瀏覽器實現了哪一個 API 版本,所以你必須實現和處理這兩個版本。
看起來是這樣的:
function requestPermission() {
return new Promise(function(resolve, reject) {
const permissionResult = Notification.requestPermission(function(result) {
// Handling deprecated version with callback.
resolve(result);
});
if (permissionResult) {
permissionResult.then(resolve, reject);
}
})
.then(function(permissionResult) {
if (permissionResult !== 'granted') {
throw new Error('Permission not granted.');
}
});
}
複製代碼
Notification.requestPermission()
調用將向用戶顯示如下提示:
一旦被受權、關閉或阻止,咱們將獲得字符串格式的結果:‘granted’
、‘default’
或 ‘denied’
。
記住,若是用戶單擊 Block
按鈕,你的 Web 應用程序將沒法再次請求用戶的許可,直到他們經過更改權限狀態手動 「unblock」 你的應用程序的限制。此選項隱藏在設置界面中。
一旦咱們註冊了 Service Worker 並得到許可權限,當你在註冊你的 Service Worker 時,咱們就能夠經過調用 registration.pushManager.subscribe()
來訂閱用戶。
整個片斷可能以下所示(包括 Service Workder 註冊):
function subscribeUserToPush() {
return navigator.serviceWorker.register('service-worker.js')
.then(function(registration) {
var subscribeOptions = {
userVisibleOnly: true,
applicationServerKey: btoa(
'BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDzkrxZJjSgSnfckjBJuBkr3qBUYIHBQFLXYp5Nksh8U'
)
};
return registration.pushManager.subscribe(subscribeOptions);
})
.then(function(pushSubscription) {
console.log('PushSubscription: ', JSON.stringify(pushSubscription));
return pushSubscription;
});
}
複製代碼
registration.pushManager.subscribe(options)
接受一個 options 對象,它包含必要參數和可選參數:
true
,不然你會獲得一個錯誤(這有歷史緣由)。DOMString
或者 ArrayBuffer
包含推送服務器用來驗證應用服務器的公鑰。你的服務器須要生成一對應用程序服務器密鑰 —— 也稱爲 VAPID 密鑰,對於你的服務器來講,它們是惟一的。它們是一對公鑰和私鑰。私鑰被祕密存儲在你的終端,而公鑰則與客戶端交換。這些密鑰容許推送服務知道哪一個應用服務器訂閱了用戶,並確保它是觸發向該特定用戶推送消息的相同服務器。
你只須要爲應用程序建立一次私鑰/公鑰對。作到這一點的方法是去完成這個 —— web-push-codelab.glitch.me/。
瀏覽器在訂閱用戶時將 applicationServerKey
(公鑰)傳遞給推送服務,這意味着推送服務能夠將應用程序的公鑰綁定到用戶的 PushSubscription
中。
狀況是這樣的:
subscribe()
來傳入你的 server 密鑰。PushSubscription
對象中,該對象經過 subscribe()
的 promise 返回。以後,不管你想什麼時候發送推送消息,你都須要建立一個包含使用應用程序服務器的專用密鑰簽名信息的 Authorization header。當推送服務收到發送推送消息的請求時,它將經過查找已經鏈接到該特定端點的公鑰來驗證頭(第二步)。
PushSubscription
包含用戶設備發送推送消息所需的全部信息。就像這樣:
{
"endpoint": "https://domain.pushservice.com/some-id",
"keys": {
"p256dh":
"BIPUL12DLfytvTajnryr3PJdAgXS3HGMlLqndGcJGabyhHheJYlNGCeXl1dn18gSJ1WArAPIxr4gK0_dQds4yiI=",
"auth":"FPssMOQPmLmXWmdSTdbKVw=="
}
}
複製代碼
endpoint
是推送服務的 URL。要觸發推送消息,請對此 URL 發送 POST 請求。
這裏的 keys
對象的值是用來加密推送消息帶過來的消息數據。
一旦用戶被訂閱而且你有 PushSubscription
,你須要將它發送到你的服務器。在那裏(在服務器上),你將這個訂閱存到數據庫中,從今之後若是你要向該用戶推送消息就使用它。
當你想向用戶發送推送消息時,你首先須要一個推送服務。你要(經過 API 調用)告訴推送服務要發送哪些數據,誰來接收數據以及其餘關於怎麼發送數據的標準。一般,此 API 調用是在你服務器上完成的。
推送服務是接收請求,驗證請求並將推送消息傳遞給對應的瀏覽器。
注意推送服務不是由你管理的 —— 它是第三方服務。你的服務器經過 API 與 推送服務進行通信。推送服務的一個例子是 Google 的 FCM。
推送服務處理全部繁重的任務,好比,若是瀏覽器處於脫機狀態,推送服務會在發送相應消息以前對消息進行排隊,等待瀏覽器的再次聯機。
每一個瀏覽器都使用它們想要的任何推送服務,這是開發者沒法控制的。
然而全部的推送服務都具備相同的 API,所以在實現過程當中不會有很大難度。
爲了得到 URL 來進行消息推送請求,你須要檢查 PushSubscription
對象中存儲的 endpoint
值。
推送服務 API 提供了一種將消息發送給用戶的方式。API 基於 Web 推送協議,它是一種定義瞭如何對推送服務進行 API 調用的 IETF 標準。
你使用推送消息發送的數據必須被加密。這樣能夠防止推送服務查看發送的數據。這很重要,由於瀏覽器是能夠決定使用哪一種推送服務的(它可能使用了一些不受信任且不夠安全的服務器)。
對於每一個推送消息,你還能夠提供下列說明:
如上所述,將消息發送到推送服務後,消息將處於掛機狀態,直到發生下列狀況之一:
當推送服務傳遞消息時,瀏覽器會接收它,解密並在 Service Worker 中分發一個 push
事件。
這裏最好的是,即便是你的網頁沒有打開,瀏覽器也能夠執行你的 Service Worker。將會發生下面的事情:
push
事件被分發給 Service Worker設置推送事件監聽器的代碼應該與用 JavaScript 編寫的任何其餘事件監聽器相似:
self.addEventListener('push', function(event) {
if (event.data) {
console.log('This push event has data: ', event.data.text());
} else {
console.log('This push event has no data.');
}
});
複製代碼
須要瞭解 Service Worker 的一點是,你沒有 Service Worker 代碼運行時長的控制權。瀏覽器決定什麼時候將其喚醒以及什麼時候終止它。
在 Service Workers 中,event.waitUntil(promise)
通知瀏覽器工做正在進行,直到 promise 肯定爲止,若是它想要完成該工做,它不該該終止 sercice worker。
這裏是處理 push
事件的例子:
self.addEventListener('push', function(event) {
var promise = self.registration.showNotification('Push notification!');
event.waitUntil(promise);
});
複製代碼
調用 self.registration.showNotification()
會向用戶發送一個通知,並返回一個 promise,只要消息展現了該 promise 就會觸發 resolve。
showNotification(title, options)
方法能夠在視覺上進行調整以適應你的需求。title
參數是一個 string
,而 options 是一個看起來像這樣的對象:
{
"//": "Visual Options",
"body": "<String>",
"icon": "<URL String>",
"image": "<URL String>",
"badge": "<URL String>",
"vibrate": "<Array of Integers>",
"sound": "<URL String>",
"dir": "<String of 'auto' | 'ltr' | 'rtl'>",
"//": "Behavioural Options",
"tag": "<String>",
"data": "<Anything>",
"requireInteraction": "<boolean>",
"renotify": "<Boolean>",
"silent": "<Boolean>",
"//": "Both Visual & Behavioural Options",
"actions": "<Array of Strings>",
"//": "Information Option. No visual affect.",
"timestamp": "<Long>"
}
複製代碼
你能夠在這裏閱讀到每一個選項內容的更多細節 — developer.mozilla.org/en-US/docs/…。
推送通知是一種能夠在有緊急、重要和時間敏感的信息須要與用戶進行分享的狀況下,吸引用戶注意的絕好方式。
例如,咱們在 SessionStack 計劃利用推送通知來提醒用戶,讓他們知道本身的產品中什麼時候發生崩潰、問題或異常。這會讓用戶當即知道出現了問題。而後他們能夠利用咱們的庫所收集的數據(如 DOM 修改、用戶交互、網絡請求、未處理異常和調試信息),以視頻的形式重現問題並查看最終發生在用戶身上的一切事情。
這個特性不只能夠幫助客戶理解和重現任何問題,並且還能夠在發生問題的第一時間通知客戶。
若是你想嘗試 SessionStack,這裏有一個免費的計劃。
掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 Android、iOS、前端、後端、區塊鏈、產品、設計、人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。