核心要點
本文將探討如何使用服務器發送事件 (SSE) 使客戶端能夠通過 HTTP 連接接收來自服務器的自動更新。我們還將探討其用途,並展示如何使用 Node.js 使用服務器發送事件的實際演示。
服務器發送事件的優勢
Web 基於請求-響應 HTTP 消息。您的瀏覽器發出 URL 請求,服務器返回數據。這可能會導致瀏覽器對圖像、CSS、JavaScript 等發出更多請求,服務器做出響應。服務器無法主動向瀏覽器發送消息,那麼它如何指示數據已更改?幸運的是,您可以使用服務器發送事件 (SSE) 添加諸如實時新聞快報、天氣預報和股票價格等功能。
使用標準 Web 技術實現實時數據更新一直是可能的:
這兩個選項都不是理想的,因為瀏覽器必須觸髮刷新。如果它過於頻繁地發出請求,則不會有任何數據更改,因此瀏覽器和服務器都會執行不必要的工作。如果它請求太慢,它可能會錯過重要的更新,而您正在關注的股票價格已經暴跌!
服務器發送事件 (SSE) 允許服務器隨時向瀏覽器推送數據:
本質上,SSE 是一個無限的數據流。可以將其視為下載一個無限大的文件,該文件以您可以攔截和讀取的小塊的形式下載。
SSE 最初於 2006 年實施,所有主要瀏覽器都支持該標準。它可能不如 WebSockets 廣為人知,但服務器發送事件更簡單,使用標準 HTTP,支持單向通信,並提供自動重新連接。本教程提供了無需第三方模塊的示例 Node.js 代碼,但 SSE 可用於其他服務器端語言,包括 PHP。
服務器發送事件快速入門
以下演示實現了一個 Node.js Web 服務器,該服務器以至少每三秒一次的隨機間隔輸出 1 到 1000 之間的隨機數。
您可以在這裡找到我們的 Node.js SSE 演示。
該代碼使用標準 Node.js http 和 url 模塊來創建 Web 服務器和解析 URL:
<code class="language-javascript">import http from "node:http"; import url from "node:url";</code>
服務器檢查傳入的 URL 請求,並在遇到 /random 路徑時做出反應:
<code class="language-javascript">const port = 8000; http.createServer(async (req, res) => { // 获取 URI 路径 const uri = url.parse(req.url).pathname; // 返回响应 switch (uri) { case "/random": sseStart(res); sseRandom(res); break; } }).listen(port); console.log(`server running: http://localhost:${port}\n\n`);</code>
它最初會使用 SSE HTTP 事件流標頭進行響應:
<code class="language-javascript">// SSE 头 function sseStart(res) { res.writeHead(200, { Content-Type: "text/event-stream", Cache-Control: "no-cache", Connection: "keep-alive" }); }</code>
另一個函數隨後發送一個隨機數,並在隨機間隔過去後調用自身:
<code class="language-javascript">// SSE 随机数 function sseRandom(res) { res.write("data: " + (Math.floor(Math.random() * 1000) + 1) + "\n\n"); setTimeout(() => sseRandom(res), Math.random() * 3000); }</code>
如果您在本地運行代碼,則可以使用終端中的 cURL 測試響應:
<code class="language-bash">$> curl -H Accept:text/event-stream http://localhost:8000/random data: 481 data: 127 data: 975</code>
按 Ctrl | Cmd 和 C 終止請求。
瀏覽器的客戶端 JavaScript 使用 EventSource 對象構造函數連接到 /random URI:
<code class="language-javascript">// 客户端 JS const source = new EventSource("/random");</code>
傳入的數據會觸發消息事件處理程序,其中 data: 後面的字符串在事件對象的 .data 屬性中可用:
<code class="language-javascript">source.addEventListener('message', e => { console.log('RECEIVED', e.data); });</code>
{ withCredentials: true }
參數以發送 Cookie。 高級服務器發送事件
SSE 不需要比上面顯示的更多代碼,但以下部分將討論其他選項。
服務器可以提供任意數量的 SSE 通道 URL。例如:
如果單個頁面顯示一個主題,這可能是實用的,但如果單個頁面顯示新聞、天氣和股票價格,則並非如此。在這種情況下,服務器必須為每個用戶維護三個連接,這可能會隨著流量的增加而導致內存問題。
另一種選擇是提供單個端點 URL,例如 /latest,它在一個通信通道上發送任何數據類型。瀏覽器可以在 URL 查詢字符串中指示感興趣的主題,例如 /latest?type=news,weather,stockprice,以便服務器可以將 SSE 響應限制為特定消息。
來自服務器的消息可以具有關聯的 event:
,它在 data:
行上方傳遞,以識別特定類型的信息:
<code class="language-javascript">import http from "node:http"; import url from "node:url";</code>
這些不會觸發客戶端的“message”事件處理程序。您必須為每種類型的事件添加處理程序。例如:
<code class="language-javascript">const port = 8000; http.createServer(async (req, res) => { // 获取 URI 路径 const uri = url.parse(req.url).pathname; // 返回响应 switch (uri) { case "/random": sseStart(res); sseRandom(res); break; } }).listen(port); console.log(`server running: http://localhost:${port}\n\n`);</code>
服務器還可以選擇在 data:
行之後發送 id:
:
<code class="language-javascript">// SSE 头 function sseStart(res) { res.writeHead(200, { Content-Type: "text/event-stream", Cache-Control: "no-cache", Connection: "keep-alive" }); }</code>
如果連接斷開,瀏覽器會在 Last-Event-ID HTTP 標頭中將最後一個 ID 發送回服務器,以便服務器可以重新發送任何錯過的消息。
最新的 ID 也可在客戶端的事件對象的 .lastEventId 屬性中獲得:
<code class="language-javascript">// SSE 随机数 function sseRandom(res) { res.write("data: " + (Math.floor(Math.random() * 1000) + 1) + "\n\n"); setTimeout(() => sseRandom(res), Math.random() * 3000); }</code>
儘管重新連接是自動的,但您的服務器可能知道在特定時間段內不需要新數據,因此無需保留活動的通信通道。服務器可以在其自身或作為最終消息的一部分發送 retry:
響應,其中包含毫秒值。例如:
<code class="language-bash">$> curl -H Accept:text/event-stream http://localhost:8000/random data: 481 data: 127 data: 975</code>
收到後,瀏覽器將放棄 SSE 連接,並在延遲時間過去後嘗試重新連接。
除了“message”和命名事件之外,您還可以在客戶端 JavaScript 中創建“open”和“error”處理程序。
當服務器連接建立時,會觸發“open”事件。它可用於運行其他配置代碼或初始化 DOM 元素:
<code class="language-javascript">// 客户端 JS const source = new EventSource("/random");</code>
當服務器連接失敗或終止時,會觸發“error”事件。您可以檢查事件對象的 .eventPhase 屬性以查看發生了什麼:
<code class="language-javascript">source.addEventListener('message', e => { console.log('RECEIVED', e.data); });</code>
請記住,無需重新連接:它會自動發生。
瀏覽器可以使用 EventSource 對象的 .close() 方法終止 SSE 通信。例如:
<code>event: news data: SSE is great! event: weather data: { "temperature": "20C", "wind": "10Kph", "rain": "25%" } event: stock data: { "symbol": "AC", "company": "Acme Corp", "price": 123.45, "increase": -1.1 }</code>
服務器可以通過以下方式終止連接:
retry:
延遲,然後只有瀏覽器可以通過創建新的 EventSource 對象來重新建立連接。
結論
服務器端事件提供了一種實現實時頁面更新的方法,這種方法可能比基於 Fetch() 的 Ajax 輪詢更容易、更實用且更輕量級。複雜性在於服務器端。您必須:
但這完全在您的控制之下,並且擴展應該不比任何其他 Web 應用程序更複雜。
唯一的缺點是 SSE 不允許您從瀏覽器向服務器發送消息(除了初始連接請求)。您可以使用 Ajax,但這對於動作遊戲等應用程序來說太慢了。對於適當的雙向通信,您需要 WebSockets。請查看如何在 Node.js 中使用 WebSockets 創建實時應用程序以了解更多信息!
以上是如何在node.js中使用服務器範圍的事件的詳細內容。更多資訊請關注PHP中文網其他相關文章!