摘要:一使用動(dòng)機(jī)與原理簡(jiǎn)述相較于移動(dòng)端本地應(yīng)用,站點(diǎn)常常缺少一項(xiàng)常用的功能推送通知。發(fā)送數(shù)據(jù)時(shí),數(shù)據(jù)必須編碼出于安全性考慮。二實(shí)現(xiàn)細(xì)節(jié)按照上一部分所說(shuō),首先進(jìn)行用戶訂閱。
一、web push 使用動(dòng)機(jī)與原理簡(jiǎn)述
相較于移動(dòng)端本地應(yīng)用,web站點(diǎn)常常缺少一項(xiàng)常用的功能:推送通知。此處的推送通知一般指由瀏覽器實(shí)現(xiàn)的消息推送,換個(gè)說(shuō)法,就是用戶在打開(kāi)瀏覽器時(shí),不需要進(jìn)入特定的網(wǎng)站,就能收到該網(wǎng)站推送而來(lái)的消息,例如:新評(píng)論,新動(dòng)態(tài)等等。
那么web push究竟是怎樣的一個(gè)流程呢,簡(jiǎn)單地說(shuō),可以分為三個(gè)步驟:
客戶端完成請(qǐng)求訂閱一個(gè)用戶的邏輯
服務(wù)端調(diào)用遵從web push協(xié)議的接口,傳送消息推送(push message)到推送服務(wù)器(該服務(wù)器由瀏覽器決定,開(kāi)發(fā)者所能做的只有控制發(fā)送的數(shù)據(jù))
推送服務(wù)器將該消息推送至對(duì)應(yīng)的瀏覽器,用戶收到該推送
第一步,客戶端請(qǐng)求訂閱用戶,過(guò)程如下:
說(shuō)明一下這三步,在第一步之前,應(yīng)用服務(wù)器需要生成應(yīng)用服務(wù)器密鑰(application server keys),其作用是標(biāo)識(shí)該服務(wù)器,保證每次發(fā)消息推送的都是同一個(gè)服務(wù)器。然后,客戶端將會(huì)請(qǐng)求用戶授權(quán)消息推送,一旦用戶授權(quán),瀏覽器就會(huì)生成一個(gè)PushScription,然后這個(gè)PushScription將會(huì)被發(fā)送至服務(wù)器,存入數(shù)據(jù)庫(kù),在后面的消息推送中使用。
第二步,應(yīng)用服務(wù)器發(fā)送web push協(xié)議標(biāo)準(zhǔn)的api,觸發(fā)推送服務(wù)器的消息推送,其中headers必須配置正確,且傳送的數(shù)據(jù)必須是比特流。
應(yīng)用服務(wù)器發(fā)送消息推送請(qǐng)求(目的是為了將更新推送到用戶的瀏覽器),為了向推送服務(wù)器發(fā)出請(qǐng)求,需要查看先前獲得的PushScription,取出其中的endpoint,即為推送服務(wù)器配置給該用戶的訪問(wèn)點(diǎn)。
一個(gè)PushScription對(duì)象如下:
{ "endpoint": "https://random-push-service.com/some-kind-of-unique-id-1234/v2/", "keys": { "p256dh" : "BNcRdreALRFXTkOOUHK1EtK2wtaz5Ry4YfYCA_0QTpQtUbVlUls0VJXg7A8u-Ts1XbjhazAkj7I99e8QcYP7DkM=", "auth" : "tBHItJI5svbpez7KI4CCXg==" } }
其中的endpoint包含了推送服務(wù)器域名,path后面的部分為推送服務(wù)器為每個(gè)用戶分配的一個(gè)標(biāo)識(shí)符。
發(fā)送數(shù)據(jù)時(shí),數(shù)據(jù)必須編碼(出于安全性考慮)。推送服務(wù)器在接收到這樣一個(gè)請(qǐng)求之后,立即開(kāi)始監(jiān)聽(tīng)用戶瀏覽器是否處于在線狀態(tài),若是,則將消息推送發(fā)送至瀏覽器。
第三步,瀏覽器端接收消息推送,觸發(fā)push事件并展示
瀏覽器在接收到推送服務(wù)器發(fā)來(lái)的推送后,將其解碼并觸發(fā)一個(gè)push事件。Service Worker由于它可以在瀏覽器頁(yè)面未打開(kāi),瀏覽器未打開(kāi)時(shí)執(zhí)行,因此一般選擇它完成web push的最后一步,即響應(yīng)push事件完成展示通知等業(yè)務(wù)邏輯。
二、web push實(shí)現(xiàn)細(xì)節(jié)按照上一部分所說(shuō),首先進(jìn)行用戶訂閱。
首先注冊(cè)一個(gè)Service Worker,若注冊(cè)成功,返回的Promise為resolve狀態(tài),如下:
function registerServiceWorker() { return navigator.serviceWorker.register("service-worker.js") .then(function(registration) { console.log("Service worker successfully registered."); return registration; }) .catch(function(err) { console.error("Unable to register service worker.", err); }); }
隨后測(cè)試window環(huán)境下是否有Notification對(duì)象(此處以chrome為例,若使用firefox,uc等瀏覽器,需要遵循其相應(yīng)標(biāo)準(zhǔn),調(diào)用對(duì)應(yīng)對(duì)象方法或引入JS SDK包),測(cè)試成功,調(diào)用Notification.requestPermission請(qǐng)求用戶授權(quán)發(fā)送推送,若授權(quán)成功,將會(huì)返回"granted"。
接下來(lái)要做的就是使用注冊(cè)好的Service Worker對(duì)象,調(diào)用pushManager.subscribe方法,從客戶端獲得剛剛所說(shuō)的PushScription對(duì)象。
function subscribeUserToPush() { return navigator.serviceWorker.register("service-worker.js") .then(function(registration) { const subscribeOptions = { userVisibleOnly: true, applicationServerKey: urlBase64ToUint8Array( "BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDzkrxZJjSgSnfckjBJuBkr3qBUYIHBQFLXYp5Nksh8U" ) }; return registration.pushManager.subscribe(subscribeOptions); }) .then(function(pushSubscription) { console.log("Received PushSubscription: ", JSON.stringify(pushSubscription)); return pushSubscription; }); }
userVisibleOnly是為了保證推送對(duì)用戶可見(jiàn),application server key則如前文所說(shuō),是推送服務(wù)器用以識(shí)別應(yīng)用服務(wù)器的密鑰,這里的密鑰包含了公鑰和私鑰,傳輸?shù)氖枪€。同時(shí),PushScription的endpoint也是在這個(gè)過(guò)程中生成的,生成公鑰和私鑰可以使用web-push庫(kù)。
這里再次說(shuō)明一下推送服務(wù)器的不可選擇性,在調(diào)用subscribe生成PushScription時(shí),瀏覽器會(huì)向它指定的中轉(zhuǎn)服務(wù)器發(fā)送請(qǐng)求來(lái)生成endpoint和其余部分,這是沒(méi)法控制的。
PushScription中的auth和p256dh是用來(lái)控制帶載荷的push message的。
獲取到PushScription對(duì)象后,將其發(fā)往應(yīng)用服務(wù)器,此處簡(jiǎn)化了存儲(chǔ),使用nedb存下PushScription并返回Promise:
function saveSubscriptionToDatabase(subscription) { return new Promise(function(resolve, reject) { db.insert(subscription, function(err, newDoc) { if (err) { reject(err); return; } resolve(newDoc._id); }); }); };
存儲(chǔ)完畢后,接下來(lái)就是開(kāi)發(fā)后臺(tái)管理邏輯,使得管理員能夠觸發(fā)向用戶推送消息的事件,應(yīng)用服務(wù)器所做的邏輯就是遍歷在數(shù)據(jù)庫(kù)中存儲(chǔ)的所有PushScription并推送消息,以下是使用web-push庫(kù)完成配置密鑰及聯(lián)系郵箱的示例:
const vapidKeys = { publicKey: "BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDzkrxZJjSgSnfckjBJuBkr3qBUYIHBQFLXYp5Nksh8U", privateKey: "UUxI4O8-FbRouAevSmBQ6o18hgE4nSG3qwvJTfKc-ls" }; webpush.setVapidDetails( "mailto:[email protected]", vapidKeys.publicKey, vapidKeys.privateKey );
不要忘了配置你在谷歌云服務(wù)(例如FCM)申請(qǐng)到的GCMApiKey:
webpush.setGCMAPIKey("");
配置完成后,就可以將subscription發(fā)送出去,使用web-push的sendNotification接口:
webpush.sendNotification(pushSubscription, "Your Push Payload Text");
推送服務(wù)器發(fā)送消息后,會(huì)觸發(fā)瀏覽器的push事件,為了控制service worker的邏輯,需要使用event.waitUntil方法,此方法接收一個(gè)promise參數(shù),在promise變?yōu)閞esolved狀態(tài)后,瀏覽器就會(huì)檢查通知是否已被展示,若是,則關(guān)閉service worker。
如果不處理未正常執(zhí)行的promise,部分瀏覽器如chrome會(huì)展示默認(rèn)消息框:
展示一個(gè)通知調(diào)用的為showNotification方法,傳的參數(shù)包括title等,如下:
var title = "Yay a message."; var body = "We have received a push message."; var icon = "/images/icon-192x192.png"; var tag = "simple-push-demo-notification-tag"; event.waitUntil( self.registration.showNotification(title, { body: body, icon: icon, tag: tag }) );
而展示notification時(shí),除了控制它的視圖層以外,也可以控制它的邏輯層,例如點(diǎn)擊消息通知后進(jìn)行某些操作等等,在先前調(diào)用showNotification時(shí)可以傳入一些參數(shù),例如,根據(jù)不同的action執(zhí)行不同的操作:
self.addEventListener("notificationclick", function(event) { if (!event.action) { // Was a normal notification click console.log("Notification Click."); return; } switch (event.action) { case "coffee-action": console.log("User ???"s coffee."); break; case "doughnut-action": console.log("User ???"s doughnuts."); break; case "gramophone-action": console.log("User ???"s music."); break; case "atom-action": console.log("User ???"s science."); break; default: console.log(`Unknown action clicked: "${event.action}"`); break; } });三、兼容性及其他問(wèn)題 與ajax輪詢、http長(zhǎng)連接、WebSocket的對(duì)比
ajax輪詢是通過(guò)客戶端不斷向服務(wù)端發(fā)送http請(qǐng)求,若有新消息就取回的模式保持?jǐn)?shù)據(jù)實(shí)時(shí)更新,但這種方式需要服務(wù)器有很快的處理速度和資源
http長(zhǎng)連接是客戶端向服務(wù)器發(fā)送請(qǐng)求后,若服務(wù)器沒(méi)有新數(shù)據(jù)要發(fā)送,就不返回response,一旦有了新數(shù)據(jù)返回了response,客戶端就立刻再發(fā)一個(gè)request,周而復(fù)始。事實(shí)上這是把http協(xié)議的不對(duì)稱性從客戶端轉(zhuǎn)移到了服務(wù)端
WebSocket是HTML5中提出的一個(gè)新標(biāo)準(zhǔn)(也可視之為協(xié)議),客戶端在發(fā)送請(qǐng)求時(shí)在請(qǐng)求頭加入額外的字段,以標(biāo)識(shí)這是一個(gè)基于WebSocket協(xié)議的連接,服務(wù)器根據(jù)這個(gè)請(qǐng)求頭生成響應(yīng),與客戶端建立起WebSocket連接,之后服務(wù)端有新消息時(shí),直接向客戶端推送即可
不同瀏覽器兼容性chrome采用的推送服務(wù)器為gcm或fcm,firefox也有自己的推送服務(wù)器
uc前些時(shí)間構(gòu)建了自己的推送服務(wù)器,引入其官網(wǎng)上的sdk包,申請(qǐng)使用后即可用于開(kāi)發(fā)
大家要是感興趣可以看看我的github~https://github.com/proempire,這個(gè)項(xiàng)目可能會(huì)繼續(xù)跟進(jìn)
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/107097.html
摘要:本文是學(xué)習(xí)與實(shí)踐系列的第五篇文章。實(shí)際上,消息推送與提醒是兩個(gè)功能和。在這一篇里,我們先來(lái)學(xué)習(xí)如何使用進(jìn)行消息推送。而當(dāng)服務(wù)端要推送消息時(shí),會(huì)使用私鑰對(duì)發(fā)送的數(shù)據(jù)進(jìn)行數(shù)字簽名,并根據(jù)數(shù)字簽名生成一個(gè)叫請(qǐng)求頭。 《PWA學(xué)習(xí)與實(shí)踐》系列文章已整理至gitbook - PWA學(xué)習(xí)手冊(cè),文字內(nèi)容已同步至learning-pwa-ebook。轉(zhuǎn)載請(qǐng)注明作者與出處。 本文是《PWA學(xué)習(xí)與實(shí)踐》系...
摘要:全稱應(yīng)用性能管理監(jiān)控后面我會(huì)通過(guò)一系列的文章來(lái)介紹的原理框架設(shè)計(jì)與實(shí)現(xiàn)等等。在應(yīng)用構(gòu)建期間,通過(guò)修改字節(jié)碼的方式來(lái)進(jìn)行字節(jié)碼插樁就是實(shí)現(xiàn)自動(dòng)化的方案之一。 showImg(https://segmentfault.com/img/bVbbRX6?w=1995&h=1273); 歡迎關(guān)注微信公眾號(hào):BaronTalk,獲取更多精彩好文! 一. 前言 性能問(wèn)題是導(dǎo)致 App 用戶流失的罪魁...
閱讀 2120·2021-10-08 10:21
閱讀 2565·2021-09-29 09:34
閱讀 3531·2021-09-22 15:51
閱讀 5013·2021-09-22 15:46
閱讀 2341·2021-08-09 13:42
閱讀 3474·2019-08-30 15:52
閱讀 2760·2019-08-29 17:13
閱讀 1584·2019-08-29 11:30