瀏覽器允許業務服務向用戶客戶端推送消息,客戶端收到推送消息后以通知的形式展示出來。業務運營人員可以通過這項功能定向給用戶推送推薦消息或者重要通知,用於提升用戶留存和用戶使用時長。
下文demo全部代碼可以在news-push-example中看到。
簡述
消息推送效果如下(當用戶沒有打開業務網站時,業務網站也能正常通知,在最終效果中有展示):
網頁消息推送流程圖如下所示:
整個過程主要可以分成訂閱和推送這兩部分,參與的角色主要有瀏覽器、瀏覽器推送服務、業務服務,下圖可以看到這三者之間的關系(圖片出自Web Push草案)
+-------+ +--------------+ +-------------+
| UA | | Push Service | | Application |
+-------+ +--------------+ | Server |
| | +-------------+
| Subscribe | |
|--------------------->| |
| Monitor | |
|<====================>| |
| | |
| Distribute Push Resource |
|-------------------------------------------->|
| | |
: : :
| | Push Message(1) |
| Push Message(2) |<---------------------|
|<---------------------| |
| | |
注:
UA:瀏覽器客戶端
Push Service:瀏覽器推送服務。不同瀏覽器廠商會部署自己的推送服務。
Application Server:業務服務。主要用於存放用戶客戶端訂閱信息和向push service推送消息兩部分工作。
訂閱流程:
- Subscribe:瀏覽器向push service發起推送訂閱,獲得訂閱信息。
- Monitor:push service存放訂閱信息,后面會使用訂閱信息向特定瀏覽器下發通知。
- Distribute Push Resource:瀏覽器將從push service獲取到的訂閱信息發給業務服務,業務服務將訂閱信息和用戶信息綁定存放起來。
推送流程:
- Push Message(1):業務服務使用Web推送協議將通知內容推送給push service。
- Push Message(2):push servicee將通知內容下發給用戶瀏覽器。
訂閱
訂閱主要任務如下圖所示(圖片來自How JavaScript works: the mechanics of Web Push Notifications)
- 網頁獲取通知授權
- 獲得在推送服務中的訂閱信息
- 將訂閱信息存放到業務服務中
網頁獲取通知授權
通過調用Notification.requestPermission()接口來拉起授權申請,授權申請的效果如下所示(每個瀏覽器效果都不太一樣,這里主要展示chrome的效果):
代碼展示如下:
Notification.requestPermission()
接口執行會返回授權結果,主要有granted
(已授權)、denied
(被拒絕)、default
(被關閉)這3中狀態。只有當授權結果為granted
時,當前網頁才能進行后面訂閱推送服務和通知消息這兩個步驟。
注意:只有在用戶沒有授權或者拒絕的情況下,瀏覽器才會拉起授權面板。
注冊service work、訂閱推送服務
通過調用service work(后面都統一叫sw)注冊對象上的訂閱api來實現訂閱推送服務,因此注冊sw任務需要在訂閱推送服務前執行。
注冊service work
通過調用navigator.serviceWorker.register()接口來注冊sw,代碼展示如下:
navigator.serviceWorker.register()
調用時,需要傳入一個運行在服務工作線程(service work)的js文件。
調用navigator.serviceWorker.register()會返回一個promise對象,我們可以在then鏈路中獲得sw注冊對象(registration)。
訂閱推送服務
通過調用registration.pushManager.subscribe()獲取訂閱信息。
在開始訂閱推送服務前,我們需要確認以下兩個任務是否完成:
- 用戶已經授權網頁通知
- sw注冊成功
1和2兩個條件之間沒有任何限制,這里使用Promise.all
來讓任務1和2並行,提升一點效率,代碼展示如下:
registration.pushManager.subscribe()
調用時需要傳入了userVisibleOnly
和applicationServerKey
這兩個參數
- userVisibleOnly:瀏覽器是否以通知的形式展示push service下發的推送消息,這里必須傳true。
- applicationServerKey:包含base64編碼的公鑰(publickKey)的DOMString或者ArrayBuffer。公鑰(publickKey)可以在web-push-codelab網頁中獲取,也可以使用web-push npm工具快速生成,同時需要把私鑰也記下來,后面
push message
步驟需要用到。
registration.pushManager.subscribe()會返回一個promise對象,我們可以在then鏈路中獲得訂閱信息,訂閱信息中主要有endpoint
和keys
這兩部分內容:
- endpoint:推送服務鏈接,需要使用post方式請求該鏈接。
- keys:用於加密推送的消息內容,防止消息內容被三方竊取。
注意:谷歌瀏覽器推送服務是FCM服務(國內被牆),使用谷歌時,需要先FQ,然后再調用registration.pushManager.subscribe api,這樣才能成功訂閱推送服務。
訂閱信息存放
前端拿到訂閱信息以后,將訂閱請求傳給業務后端,業務后端得到訂閱信息以后,將訂閱信息和用戶關聯起來,等到需要向用戶推送消息的時候再提取該用戶對應的訂閱信息。
推送
推送的主要流程如下圖所示(圖片來自How JavaScript works: the mechanics of Web Push Notifications):
- 業務服務向push service推送消息
- push service向瀏覽器推送消息
- 瀏覽器展示消息
推送消息
當業務方需要向用戶推送通知時,業務服務需要從數據庫中取出用戶的訂閱信息,然后帶着消息內容去請求訂閱信息中的推送服務鏈接。請求推送服務時必須依據Web推送協議,node應用中可以使用web-push工具幫助我們快速實現請求推送服務功能,代碼如下:
實現流程:
- 給
web-push
工具設置VAPID keys
。 - 讀取服務器中存放的訂閱信息。訂閱信息中有
endpoint
、key
。 - 生成推送消息內容
pushData
、生成web-push
配置options
信息。- 推送消息內容必須是一個沒有特殊字符的字符串或者是一個buffer對象,如果想傳輸json數據,可以將json數據轉成buffer對象后再傳輸
- options中的
proxy
字段是請求推送服務(谷歌、火狐的消息推送服務是被牆了的)時設置的代理(如果業務服務部署在大陸以外,就可以不用設置代理),其他參數可以在web-push中看到詳解
- 調用
webPush.sendNotification()
向push service推送消息
注意:由於谷歌和火狐消息推送服務被牆了,我們可以在使用web-push工具推送消息時設置proxy參數來進行請求代理,也可以將業務服務部署在大陸以外。
瀏覽器通知
push service根據業務服務推送過來的客戶端訂閱信息,獲取到具體的用戶瀏覽器客戶端后,將消息下發給瀏覽器,瀏覽器將消息解密以后,會去觸發相應的service work的push事件,此時只要sw文件中監聽push事件,就能夠獲取到業務服務端推送的消息內容。監聽push代碼展示如下:
service work中通過調用self.registration.showNotification()
函數拉起瀏覽器通知,代碼中showNotification
函數調用傳參與通知UI的對應關系如下面兩張圖所示(更多參數可以在showNotification mdn中看到):
當用戶點擊通知時,會觸發sw中的notificationclick
事件,同時,瀏覽器會將之前設置的action值一並傳入回調函數,代碼展示如下:
消息推送效果
結語
國內谷歌用戶,如果沒有FQ,瀏覽器客戶端將無法訂閱FCM服務以及無法收到FCM服務下發的消息推送。業務服務在請求推送服務的時候,也需要設置設置代理,或者將業務服務部署到大陸外的網絡。
國內火狐用戶,只要業務服務能夠正常請求瀏覽器推送服務,那么用戶瀏覽器客戶端就能正常收到推送服務下發的消息。
暫時測試國內的瀏覽器,但是據了解,國內的殺毒軟件可能將瀏覽器通知當病毒來掃描,qq瀏覽器有開發推送服務但是好像沒有開放出來使用。