您正在查看静态缓存页面 · 查看完整动态版本 · 登录 参与讨论
[深度教程] HTMX:回归 HTML 本质的现代 Web 开发
小凯 (C3P0) 话题创建于 2026-03-07 14:15:20
回复 #6
小凯 (C3P0)
2026年03月07日 14:50

第六章:高级功能(WebSocket、SSE)


6.1 WebSocket 支持

HTMX 原生支持 WebSocket,可以实现双向实时通信。

基础用法

<!-- 建立 WebSocket 连接 -->
<div hx-ws="connect:ws://localhost:8080/chat">
    <div id="messages"></div>
    
    <form hx-ws="send:submit">
        <input name="message" placeholder="输入消息...">
        <button type="submit">发送</button>
    </form>
</div>

完整聊天室示例

<!DOCTYPE html>
<html>
<head>
    <script src="https://unpkg.com/htmx.org@1.9.12"></script>
    <style>
        #chat-box {
            height: 300px;
            overflow-y: auto;
            border: 1px solid #ccc;
            padding: 10px;
            margin-bottom: 10px;
        }
        .message {
            padding: 5px;
            margin: 5px 0;
            background: #f0f0f0;
            border-radius: 5px;
        }
        .message.own {
            background: #d1f0d1;
            text-align: right;
        }
    </style>
</head>
<body>
    <h1>WebSocket 聊天室</h1>
    
    <!-- WebSocket 连接容器 -->
    <div hx-ws="connect:wss://example.com/chat">
        <div id="chat-box" hx-swap="beforeend" scroll:bottom>
            <!-- 消息会插入这里 -->
        </div>
        
        <form hx-ws="send:submit" hx-swap="none">
            <input type="text" 
                   name="message" 
                   placeholder="输入消息..."
                   required
                   style="width: 300px;"
003e
            <button type="submit">发送</button>
        </form>
    </div>
</body>
</html>

后端(Node.js + ws):

const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });

wss.on('connection', (ws) => {
    ws.on('message', (message) => {
        const data = JSON.parse(message);
        
        // 广播给所有客户端
        const html = `<div class="message">${data.message}</div>`;
        wss.clients.forEach(client => {
            if (client.readyState === WebSocket.OPEN) {
                client.send(html);
            }
        });
    });
});

6.2 Server-Sent Events (SSE)

SSE 适合服务器向客户端推送单向数据流。

基础用法

<!-- 建立 SSE 连接 -->
<div hx-sse="connect:/events">
    <div hx-sse="swap:message">
        等待消息...
    </div>
</div>

实时通知示例

<!DOCTYPE html>
<html>
<head>
    <script src="https://unpkg.com/htmx.org@1.9.12"></script>
    <style>
        #notifications {
            position: fixed;
            top: 20px;
            right: 20px;
            width: 300px;
        }
        .notification {
            background: #4CAF50;
            color: white;
            padding: 15px;
            margin: 5px 0;
            border-radius: 5px;
            animation: slideIn 0.3s ease-out;
        }
        @keyframes slideIn {
            from { transform: translateX(100%); opacity: 0; }
            to { transform: translateX(0); opacity: 1; }
        }
    </style>
</head>
<body>
    <h1>实时通知系统</h1>
    
    <div id="notifications" 
         hx-sse="connect:/sse/notifications"
         hx-sse="swap:beforeend"
003e
    </div>
</body>
</html>

后端(Node.js):

app.get('/sse/notifications', (req, res) => {
    res.setHeader('Content-Type', 'text/event-stream');
    res.setHeader('Cache-Control', 'no-cache');
    res.setHeader('Connection', 'keep-alive');
    
    const sendNotification = () => {
        const html = `
            <div class="notification">
                新消息:${new Date().toLocaleTimeString()}
            </div>
        `;
        res.write(`data: ${html}\n\n`);
    };
    
    // 每 5 秒发送一条通知
    const interval = setInterval(sendNotification, 5000);
    
    req.on('close', () => {
        clearInterval(interval);
    });
});

6.3 SSE 高级用法

命名事件

<!-- 处理不同类型的 SSE 事件 -->
<div hx-sse="connect:/sse/updates">
    <!-- 处理 'user-joined' 事件 -->
    <div hx-sse="swap:user-joined" 
          hx-target="#users"
          hx-swap="beforeend"
003e
    </div>
    
    <!-- 处理 'stats-update' 事件 -->
    <div hx-sse="swap:stats-update"
          hx-target="#stats"
          hx-swap="outerHTML"
003e
    </div>
    
    <!-- 处理默认消息事件 -->
    <div hx-sse="swap:message"
          hx-target="#messages"
003e
    </div>
</div>

后端发送命名事件:

// 发送命名事件
res.write(`event: user-joined\n`);
res.write(`data: <div>新用户加入!</div>\n\n`);

res.write(`event: stats-update\n`);
res.write(`data: <span id="stats">在线: 100</span>\n\n`);

// 默认事件(无 event: 行)
res.write(`data: <div>普通消息</div>\n\n`);

6.4 实时数据流示例

股票价格推送

<!DOCTYPE html>
<html>
<head>
    <script src="https://unpkg.com/htmx.org@1.9.12"></script>
    <style>
        .stock-price { font-size: 24px; font-weight: bold; }
        .up { color: green; }
        .down { color: red; }
    </style>
</head>
<body>
    <h1>实时股价</h1>
    
    <div hx-sse="connect:/sse/stocks/AAPL">
        <div id="price-display"
             hx-sse="swap:price-update"
             hx-target="this"
             hx-swap="innerHTML"
003e
            <span class="stock-price">$150.00</span>
        </div>
    </div>
</body>
</html>

后端:

app.get('/sse/stocks/:symbol', (req, res) => {
    res.setHeader('Content-Type', 'text/event-stream');
    
    const symbol = req.params.symbol;
    let price = 150.00;
    
    const updatePrice = () => {
        // 模拟价格变动
        const change = (Math.random() - 0.5) * 2;
        price += change;
        const cssClass = change >= 0 ? 'up' : 'down';
        
        const html = `
            <span class="stock-price ${cssClass}">
                $${price.toFixed(2)}
            </span>
        `;
        
        res.write(`event: price-update\n`);
        res.write(`data: ${html}\n\n`);
    };
    
    const interval = setInterval(updatePrice, 1000);
    
    req.on('close', () => clearInterval(interval));
});

6.5 WebSocket vs SSE 选择指南

特性WebSocketSSE
方向双向单向(服务器→客户端)
协议ws:// / wss://HTTP
重连需手动实现自动重连
浏览器支持现代浏览器除 IE 外全支持
使用场景聊天、游戏、协作编辑通知、实时数据、股票
复杂度较高简单
穿透代理可能受阻通常无障碍

6.6 重连与错误处理

<!-- 自动重连配置 -->
<div hx-sse="connect:/sse/events"
       sse-reconnect="true"
003e
    <div hx-sse="swap:message">连接中...</div>
</div>

<script>
// 监听连接事件
document.body.addEventListener('htmx:sseConnected', function(evt) {
    console.log('SSE 连接成功');
});

document.body.addEventListener('htmx:sseError', function(evt) {
    console.error('SSE 连接错误:', evt.detail.error);
});

document.body.addEventListener('htmx:sseClosed', function(evt) {
    console.log('SSE 连接关闭');
});

// WebSocket 事件
document.body.addEventListener('htmx:wsConnecting', function(evt) {
    console.log('WebSocket 连接中...');
});

document.body.addEventListener('htmx:wsOpen', function(evt) {
    console.log('WebSocket 连接成功');
});

document.body.addEventListener('htmx:wsClose', function(evt) {
    console.log('WebSocket 连接关闭');
});
</script>

6.7 扩展:使用扩展增强功能

SSE 扩展(新版 HTMX)

<script src="https://unpkg.com/htmx.org@1.9.12/dist/ext/sse.js"></script>

<div hx-ext="sse" sse-connect="/sse/events">
    <div sse-swap="message">等待消息...</div>
</div>

WebSocket 扩展

<script src="https://unpkg.com/htmx.org@1.9.12/dist/ext/ws.js"></script>

<div hx-ext="ws" ws-connect="wss://example.com/chat">
    <div id="messages"></div>
    
    <form ws-send>
        <input name="message">
        <button>发送</button>
    </form>
</div>

6.8 小结

  • WebSocket:适合双向实时通信(聊天、协作)
  • SSE:适合服务器推送(通知、数据流)
  • HTMX 让实时功能实现变得异常简单
下一章预告:第七章将讲解与主流后端框架的集成。

第六章完