htmx 伺服器發送事件 (SSE) 擴充功能

伺服器發送事件 (Server Sent Events) 擴充功能直接從 HTML 連接到 EventSource。它管理與您 Web 伺服器的連線,監聽伺服器事件,然後將其內容即時交換到您的 htmx 網頁中。

SSE 是 WebSockets 的輕量級替代方案,它透過現有的 HTTP 連線工作,因此很容易透過 Proxy 伺服器和防火牆使用。請記住,SSE 是一種單向服務,因此一旦建立連線,您就無法向 SSE 伺服器發送任何訊息。如果您需要雙向通訊,那麼您應該考慮改用 WebSockets

此擴充功能取代了 htmx 先前版本中內建的實驗性 hx-sse 屬性。如需從舊版本遷移的協助,請參閱本頁底部的遷移指南。

使用以下屬性來設定 SSE 連線的行為

安裝


<script src="https://unpkg.com/htmx-ext-sse@2.2.2/sse.js"></script>

用法


<div hx-ext="sse" sse-connect="/chatroom" sse-swap="message">
    Contents of this box will be updated in real time
    with every SSE message received from the chatroom.
</div>

連線到 SSE 伺服器

要連線到 SSE 伺服器,請使用 hx-ext="sse" 屬性在該 HTML 元素上安裝擴充功能,然後將 sse-connect="<url>" 新增至該元素以建立連線。

在設計伺服器應用程式時,請記住 SSE 的運作方式與任何 HTTP 請求相同。雖然您在建立連線後無法向伺服器發送任何訊息,但您可以將參數與您的請求一起發送到伺服器。因此,您不僅可以連線到 https://my-server/chat-updates 的伺服器,還可以連線到 https://my-server/chat-updates?friends=true&format=detailed。這允許您的伺服器根據客戶端的需求自訂其回應。

接收具名事件

SSE 訊息由事件名稱和資料包組成。訊息中不允許其他中繼資料。以下是一個範例

event: EventName
data: <div>Content to swap into your HTML page.</div>

我們將使用 sse-swap 屬性來監聽此事件,並將其內容交換到我們的網頁中。


<div hx-ext="sse" sse-connect="/event-source" sse-swap="EventName"></div>

請注意,伺服器訊息中的名稱 EventName 必須與 sse-swap 屬性中的值相符。您的伺服器可以使用任意數量的不同事件名稱,但請小心:瀏覽器只能監聽已明確命名的事件。因此,如果您的伺服器傳送名為 ChatroomUpdate 的事件,但您的瀏覽器只監聽名為 ChatUpdate 的事件,則會捨棄額外的事件。

接收未命名事件

也可以發送沒有任何事件名稱的 SSE 訊息。在這種情況下,瀏覽器會使用預設名稱 message 來代替。上述相同的規則仍然適用。如果您的伺服器發送未命名的訊息,則您必須透過包含 sse-swap="message" 來監聽它。沒有選項可以使用全域捕獲名稱。以下是它的樣子

data: <div>Content to swap into your HTML page.</div>

<div hx-ext="sse" sse-connect="/event-source" sse-swap="message"></div>

接收多個事件

您也可以從單一 EventSource 監聽多個事件(具名或未命名)。監聽器必須是:1) 包含 hx-extsse-connect 屬性的相同元素,或 2) 包含 hx-extsse-connect 屬性的元素的子元素。


Multiple events in the same element
<div hx-ext="sse" sse-connect="/server-url" sse-swap="event1,event2"></div>

Multiple events in different elements (from the same source).
<div hx-ext="sse" sse-connect="/server-url">
    <div sse-swap="event1"></div>
    <div sse-swap="event2"></div>
</div>

觸發伺服器回呼

當伺服器發送事件的連線已建立時,子元素可以使用特殊的 hx-trigger 語法 sse:<event_name> 來監聽這些事件。當與 hx-get 或類似的屬性結合使用時,將會觸發元素發出請求。

以下是一個範例


<div hx-ext="sse" sse-connect="/event_stream">
    <div hx-get="/chatroom" hx-trigger="sse:chatter">
        ...
    </div>
</div>

此範例建立與 event_stream 端點的 SSE 連線,然後在每次看到 chatter 事件時,觸發對 /chatroom URL 的 GET 請求。

自動重新連線

如果 SSE Event Stream 非預期關閉,瀏覽器應該會嘗試自動重新連線。然而,在極少數情況下,這不起作用,您的瀏覽器可能會處於掛起狀態。此擴充功能會在瀏覽器的自動重新連線之上新增其自己的重新連線邏輯(使用 指數退避演算法),以便您的 SSE 流始終盡可能可靠。

使用示範伺服器測試 SSE 連線

Htmx 包含一個以 Node.js 撰寫的示範 SSE 伺服器,可協助您查看實際運作的 SSE,並開始引導您自己的 SSE 程式碼。它位於 htmx 發行版的 /test/ws-sse 資料夾中。請查看 /test/ws-sse/README.md 以取得執行和使用測試伺服器的說明。

從先前版本遷移

htmx 的先前版本使用內建標籤 hx-sse 來實作伺服器發送事件。此程式碼已遷移到擴充功能中。以下是您遷移到此版本需要採取的步驟

舊屬性新屬性備註
hx-sse=""hx-ext="sse"使用 hx-ext="sse" 屬性將 SSE 擴充功能安裝到任何 HTML 元素中。
hx-sse="connect:<url>"sse-connect="<url>"將新屬性 sse-connect 新增至標籤,以指定 Event Stream 的 URL。此屬性必須與 hx-ext 屬性位於相同的標籤中。
hx-sse="swap:<EventName>"sse-swap="<EventName>"將新屬性 sse-swap 新增至將透過 SSE 擴充功能交換的任何元素。此屬性必須放置在包含 hx-ext 屬性的標籤內部
hx-trigger="sse:<EventName>"無變更任何 hx-trigger 屬性都不需要變更。擴充功能將識別這些屬性,並為任何以 sse: 為字首的事件新增監聽器

監聽此擴充功能發出的事件

此擴充功能會發出數個事件。您可以像這樣監聽這些事件

document.body.addEventListener('htmx:sseBeforeMessage', function (e) {
    // do something before the event data is swapped in
})

每個事件物件都有一個 detail 欄位,其中包含事件的詳細資訊。

htmx:sseOpen

當成功建立 SSE 連線時,會發出此事件。

詳細資訊

htmx:sseError

當無法建立 SSE 連線時,會發出此事件。

詳細資訊

htmx:sseBeforeMessage

此事件會在將 SSE 事件資料交換到 DOM 之前立即發出。如果您不想交換,請在事件上呼叫 preventDefault()。此外,detail 欄位是 MessageEvent - 這是 EventSource 在收到 SSE 訊息時建立的事件。

詳細資訊

htmx:sseMessage

此事件會在將 SSE 事件資料交換到 DOM 後發出。detail 欄位是 MessageEvent - 這是 EventSource 在收到 SSE 訊息時建立的事件。

htmx:sseClose

此事件在三種不同的關閉情況下發出。為了控制情況,使用者可以控制 evt.detail.sseclose 屬性。

document.body.addEventListener('htmx:sseClose', function (e) {
    const reason = e.detail.type
    switch (reason) {
        case "nodeMissing":
            // Parent node is missing and therefore connection was closed
        ...
        case "nodeReplaced":
            // Parent node replacement caused closing of connection
        ...
        case "message":
            // connection was closed due to reception of message sse-close
        ...
    }
})
詳細資訊

其他 SSE 資源