前言
前面我們已經聊了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,如果浏覽器不相容,我們也可以對它做相容。從以上的介紹可以看出它是單向的,連接配接後隻能是伺服器向浏覽器推送,是以它非常适合僅有查詢的需求,像球賽直播、股票類的需求。
轉自https://www.toutiao.com/i6763041921074987534/?timestamp=1592920385&app=news_article&group_id=6763041921074987534&use_new_style=0&req_id=202006232153040100150451400728596D
喜歡這篇文章?歡迎打賞~~