htmx 服务器发送事件 (SSE) 扩展

服务器发送事件 扩展直接从 HTML 连接到 EventSource。它管理与 Web 服务器的连接,监听服务器事件,然后将其实时内容替换到您的 htmx 网页中。

SSE 是 WebSockets 的轻量级替代方案,它通过现有的 HTTP 连接工作,因此易于通过代理服务器和防火墙使用。请注意,SSE 是单向服务,因此一旦建立连接,您无法向 SSE 服务器发送任何消息。如果您需要双向通信,则应考虑使用 WebSockets

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

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

安装

安装 sse 的最快方式是通过 CDN 加载。请记得始终在扩展之前包含核心 htmx 库,并启用扩展

<head>
    <script src="https://cdn.jsdelivr.net/npm/htmx.org@2.0.6/dist/htmx.min.js" integrity="sha384-HGfztofotfshcF7+8n44JQL2oJmowVChPTg48S+jvZoztPfvwD79OC/LTtG6dMp+" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/htmx-ext-sse@2.2.2" integrity="sha384-Y4gc0CK6Kg+hmulDc6rZPJu0tqvk7EWlih0Oh+2OkAi1ZDlCbBDCQEE2uVk472Ky" crossorigin="anonymous"></script>
</head>
<body hx-ext="sse">

未压缩版本也可在 https://cdn.jsdelivr.net/npm/htmx-ext-sse/dist/sse.js 获取。

虽然 CDN 方法简单,但您可能需要考虑在生产环境中不使用 CDN。下一个最简单的安装 sse 的方法就是将其复制到您的项目中。从 https://cdn.jsdelivr.net/npm/htmx-ext-sse 下载扩展,将其添加到项目的适当目录中,并在需要时使用 <script> 标签包含它。

对于 npm 风格的构建系统,您可以通过 npm 安装 sse

npm install htmx-ext-sse

安装后,您需要使用适当的工具将 node_modules/htmx-ext-sse/dist/sse.js(或 .min.js)打包。例如,您可以将扩展与来自 node_modules/htmx.org/dist/htmx.js 的 htmx 核心以及项目特定代码一起打包。

如果您使用打包器管理 JavaScript(例如 Webpack、Rollup):

import `htmx.org`;
import `htmx-ext-sse`; 

使用


<div hx-ext="sse" sse-connect="/chatroom" sse-swap="message">
    此框的内容将实时更新
    每次从聊天室接收到 SSE 消息时。
</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>要替换到 HTML 页面中的内容。</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>要替换到 HTML 页面中的内容。</div>

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

接收多个事件

您还可以从单个 EventSource 监听多个事件(命名或未命名)。监听器必须是:1) 包含 hx-extsse-connect 属性的同一元素,或 2) 包含 hx-extsse-connect 属性的元素的子元素。


同一元素中的多个事件
<div hx-ext="sse" sse-connect="/server-url" sse-swap="event1,event2"></div>

不同元素中的多个事件(来自同一源)。
<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 事件流意外关闭,浏览器应尝试自动重新连接。然而,在极少数情况下,这可能不起作用,您的浏览器可能会挂起。此扩展在其自动重连逻辑之上添加了自己的重连逻辑(使用指数退避算法),以确保您的 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>"添加一个新属性 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) {
    // 在事件数据被替换到 DOM 之前执行某些操作
})

每个事件对象都有一个 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":
            // 父节点缺失,因此连接被关闭
        ...
        case "nodeReplaced":
            // 父节点替换导致连接关闭
        ...
        case "message":
            // 由于接收到 sse-close 消息,连接被关闭
        ...
    }
})
详情

更多 SSE 资源