服务器发送事件
扩展直接从 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。如果您想向最终会停止的客户端发送信息,这可能会有帮助。安装 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):
htmx.org
和 htmx-ext-sse
index.js
中import `htmx.org`;
import `htmx-ext-sse`;
<div hx-ext="sse" sse-connect="/chatroom" sse-swap="message">
此框的内容将实时更新
每次从聊天室接收到 SSE 消息时。
</div>
要连接到 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-ext
和 sse-connect
属性的同一元素,或 2) 包含 hx-ext
和 sse-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 流始终尽可能可靠。
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 连接时,此事件被分发。
detail.elt
- 设置 SSE 连接的元素。这是具有 sse-connect
属性的元素。detail.source
- EventSource 对象。htmx:sseError
当无法建立 SSE 连接时,此事件被分发。
detail.error
- 创建 EventSource 时发生的错误。detail.source
- EventSource。htmx:sseBeforeMessage
在 SSE 事件数据被替换到 DOM 之前,此事件被分发。如果不想替换,请在事件上调用 preventDefault()
。此外,detail
字段是一个 MessageEvent - 这是 EventSource 在接收到 SSE 消息时创建的事件。
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":
// 父节点缺失,因此连接被关闭
...
case "nodeReplaced":
// 父节点替换导致连接关闭
...
case "message":
// 由于接收到 sse-close 消息,连接被关闭
...
}
})
detail.elt
- 替换目标。