詳解前端如何讓服務(wù)器主動(dòng)向?yàn)g覽器推送數(shù)據(jù)
前言
前面我們已經(jīng)聊了ajax,它的特點(diǎn)是瀏覽器必須先發(fā)起請(qǐng)求,服務(wù)器才能給出對(duì)應(yīng)的響應(yīng),想一想能不能讓服務(wù)器主動(dòng)向?yàn)g覽器推送數(shù)據(jù)呢?那么這篇文章我們來聊一聊服務(wù)器推送功能。
輪詢
假設(shè)你現(xiàn)在需要去做一個(gè)球賽直播頁面,一個(gè)主播在后臺(tái)文字直播比賽,那么這就要求解說數(shù)據(jù)盡可能的實(shí)時(shí)到達(dá)瀏覽器,那么我們?nèi)绾谓鉀Q呢?
最容易想到的就是用ajax輪詢,寫個(gè)定時(shí)器,每隔幾秒鐘去后端請(qǐng)求一次數(shù)據(jù),這當(dāng)然是可以的,但是這種方式并不是很優(yōu)雅。因?yàn)闉g覽器每次需要主動(dòng)去詢問服務(wù)器有沒有數(shù)據(jù),如果反過來服務(wù)器有數(shù)據(jù)能主動(dòng)告之瀏覽器豈不是更好?
初識(shí)推送
那么如何能讓服務(wù)器自己主動(dòng)推送數(shù)據(jù)呢?下面我們先實(shí)現(xiàn)一下服務(wù)端的代碼,如下:
圖1
如圖1,我們用express起了一個(gè)小服務(wù),每當(dāng)服務(wù)器接收到請(qǐng)求在響應(yīng)時(shí)就會(huì)把Content-type設(shè)置為text/event-stream,這是實(shí)現(xiàn)服務(wù)器推送的關(guān)鍵,它表示這次鏈接采用流實(shí)現(xiàn)并且整個(gè)頁面生命周期都保持這一個(gè)鏈接的打開狀態(tài)!
頁面上的實(shí)現(xiàn)和普通ajax類似,但也有點(diǎn)不同,如下:
圖2
如圖2,獲取數(shù)據(jù)時(shí)的狀態(tài)是在3,因?yàn)榇随溄右恢碧幱诖蜷_狀態(tài)。
當(dāng)你同時(shí)運(yùn)行服務(wù)端和客戶端的代碼時(shí),你會(huì)發(fā)現(xiàn)瀏覽器控制臺(tái)會(huì)一直打印123,此刻我們就已經(jīng)實(shí)現(xiàn)了服務(wù)器推送的功能。
SSE
根據(jù)上面介紹的服務(wù)器推送的特點(diǎn),瀏覽器自身也實(shí)現(xiàn)了這樣的接口——SSE。
我們先看一下瀏覽器端如何使用,如下:
圖3
如圖3,使用EventSource創(chuàng)建一個(gè)實(shí)例對(duì)象,參數(shù)是流實(shí)現(xiàn)的接口,下面綁定了幾個(gè)事件,
- open,當(dāng)連接上之后就會(huì)立即觸發(fā);
- message,服務(wù)器向客戶端發(fā)送數(shù)據(jù)的默認(rèn)事件,通過e.data可以獲取到數(shù)據(jù);
- foo,自定義事件(SSE支持自定義事件);
- error,當(dāng)鏈接發(fā)生錯(cuò)誤時(shí)觸發(fā)。
下面我們?cè)倏匆幌路?wù)端實(shí)現(xiàn),如下:
圖4
如圖4所示,和圖1中的代碼雷同,先起一下服務(wù)端,再打開頁面,看一下現(xiàn)象:
圖5
從現(xiàn)象看我們已經(jīng)實(shí)現(xiàn)了服務(wù)器推送,符合預(yù)期,很好!但是要注意圖4中響應(yīng)數(shù)據(jù)時(shí)的寫法,以data: 開頭會(huì)默認(rèn)觸發(fā)頁面中message事件,以\n\n結(jié)尾結(jié)束一次推送,如果一行寫不下可以用\n分割;
下面我們看看如何觸發(fā)自定義事件,代碼如下:
圖6
如圖6,我們加一行字符串——'event:' + 事件名 + '\n',這樣就會(huì)觸發(fā)頁面中的foo事件而不是message事件,現(xiàn)象如下:
圖7
據(jù)說SSE具有斷線重連能力,我們?cè)囈幌拢纯词钦媸羌?
圖8
圖8證明傳言不虛,當(dāng)把服務(wù)關(guān)掉,頁面在不停的嘗試重連,當(dāng)服務(wù)再次啟動(dòng),頁面就會(huì)立馬鏈接上。如果服務(wù)端在發(fā)送數(shù)據(jù)時(shí)加一行字符串——"retry: " + 時(shí)間 + "\n\n" ,可以設(shè)置斷開后重新再鏈接的間隔時(shí)間,這個(gè)就不演示了。
到目前為止,推送還是無狀態(tài)的,如果鏈接斷開了,再次重連,服務(wù)器是不知道先前已經(jīng)推送了什么數(shù)據(jù),為了解決這個(gè)問題,在每次推送時(shí)可以加一行字符串——"id: " + 序號(hào) + "\n",如下:
圖9
我們?cè)賮砜匆幌卢F(xiàn)象,如下:
圖10
這個(gè)序號(hào)在頁面上用事件對(duì)象的lastEventId屬性可以拿到,當(dāng)下一次重新連接時(shí),瀏覽器會(huì)把最后一次序號(hào)放在請(qǐng)求頭的Last-Event-ID字段中,所以服務(wù)端可以通過這個(gè)請(qǐng)求頭屬性獲取之前數(shù)據(jù)推送的情況。
圖11
總結(jié)
這篇文章主要講解SSE如何使用,它是瀏覽器自帶的API,如果瀏覽器不兼容,我們也可以對(duì)它做兼容。從以上的介紹可以看出它是單向的,連接后只能是服務(wù)器向?yàn)g覽器推送,所以它非常適合僅有查詢的需求,像球賽直播、股票類的需求。