htmx Server Sent Events (SSE) 扩展

Server Sent Events 扩展程序可直接从 HTML 连接到EventSource。它管理与你的 Web 服务器的连接,监听服务器事件,然后实时将其内容交换到你的 htmx 网页中。

SSE 是 WebSockets 的轻量级替代方案,可在现有 HTTP 连接上运行,因此可以轻松通过代理服务器和防火墙使用。请记住,SSE 是一种单向服务,因此一旦建立连接,你就无法向 SSE 服务器发送任何消息。如果你需要双向通信,那么你应该考虑使用WebSockets。

此扩展取代了 htmx 先前版本中内置的实验性 hx-sse 属性。如需从旧版本迁移的帮助,请参阅本页底部的迁移指南。

使用以下属性来配置 SSE 连接的行为方式:

  • sse-connect="<url>" - SSE 服务器的 URL。
  • sse-swap="<message-name>" - 要交换到 DOM 中的消息的名称。
  • hx-trigger="sse:<message-name>" - SSE 消息还可以使用hx-trigger 属性触发 HTTP 回调。
  • sse-close=<message-name> - 收到该消息后正常关闭 EventStream。如果你想要向最终会停止的客户端发送信息,这可能会有所帮助。

安装

<script src="https://unpkg.com/[email protected]/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-ext 和 sse-connect 属性的相同元素,或 2) 包含 hx-ext 和 sse-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 连接,然后每当看到事件时就会触发对url /chatroomchatter 的 GET。

自动重新连接

如果 SSE 事件流意外关闭,浏览器应该会尝试自动重新连接。然而,在极少数情况下,这不起作用,你的浏览器可能会挂起。此扩展在浏览器的自动重新连接之上添加了自己的重新连接逻辑(使用指数退避算法),以便你的 SSE 流始终尽可能可靠。

使用演示服务器测试 SSE 连接

htmx 包含一个用 Node.js 编写的演示 SSE 服务器,可帮助你了解 SSE 的运行情况,并开始引导你自己的 SSE 代码。它位于 htmx-extensions 存储库的 /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>" 向指定事件流 URL 的标记添加 sse-connect 新属性。此属性必须与 hx-ext 属性位于同一标记中。
hx-sse="swap:<EventName>" sse-swap="<EventName>" 向将通过 SSE 扩展进行交换的任何元素添加 sse-swap 新属性。此属性必须放置在包含 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 连接成功建立时,会分派此事件。

detail
  • detail.elt - 设置了 SSE 连接的元素。这是具有该 sse-connect 属性的元素。
  • detail.source - EventSource对象。

htmx:sseError

当无法建立 SSE 连接时,会触发此事件。

detail
  • detail.error - 创建EventSource时发生的错误。
  • detail.source - 事件源。

htmx:sseBeforeMessage

此事件在 SSE 事件数据交换到 DOM 之前触发。如果你不想交换,请调用preventDefault() 事件。此外,该 detail 字段是一个 MessageEvent - 这是EventSource 在收到 SSE 消息时创建的事件。

detail
  • detail.elt- 交换目标。

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
        ...
    }
})
detail
  • detail.elt - 交换目标。

其他 SSE 资源