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