使用Node/Express将命令行输出的Twitch聊天内容展示到HTML页面
Twitch聊天流网页展示实现方案
核心问题说明
现有实现基于普通HTTP请求响应逻辑,服务端返回完twitch-chat.html页面后,和前端的连接就会断开,后续tmi.js拉取到的新聊天消息没有持续传输通道,因此只能在服务端控制台打印,无法同步到网页。要实现实时消息推送,采用WebSocket方案即可,新手优先选Socket.IO,对接成本极低,不需要手写底层WebSocket逻辑。
具体实现步骤
1. 安装所需依赖
执行命令安装Socket.IO:
npm install socket.io
注意:原有代码里bodyParser.urlEncoded存在拼写错误,正确写法是bodyParser.urlencoded,否则会导致表单参数解析失败。原有index.html表单逻辑不需要修改,可直接复用。
2. 改造后端服务(app.js)
把Express服务和Socket.IO绑定,前端连接后传入要监听的频道名,服务端创建tmi客户端拉取聊天消息,每收到一条消息就通过WebSocket推送给前端。
改造后的完整代码:
const express = require("express"); const bodyParser = require("body-parser"); const tmi = require("tmi.js"); const { createServer } = require("http"); const { Server } = require("socket.io"); const app = express(); // 创建HTTP服务绑定Socket.IO const httpServer = createServer(app); const io = new Server(httpServer); app.use(express.static("public")); // 修正原拼写错误:urlencoded 不是 urlEncoded app.use(bodyParser.urlencoded({ extended: true })); const port = 3000; // 首页表单路由 app.get("/", (req, res) => { res.sendFile(__dirname + "/index.html"); }); // 表单提交处理,把频道名拼到URL参数传给前端 app.post("/", (req, res) => { const channelName = req.body.channelID.trim(); res.redirect(`/chat?channel=${encodeURIComponent(channelName)}`); }); // 聊天页渲染路由 app.get("/chat", (req, res) => { res.sendFile(__dirname + "/twitch-chat.html"); }); // 处理WebSocket连接 io.on("connection", (socket) => { let twitchClient = null; // 接收前端传过来的要监听的频道名 socket.on("join-channel", (channelName) => { // 初始化tmi客户端 twitchClient = new tmi.Client({ connection: { secure: true, reconnect: true }, channels: [channelName] }); twitchClient.connect(); // 收到Twitch聊天消息时推送给前端 twitchClient.on('message', (channel, tags, message, self) => { const chatItem = { username: tags['display-name'], content: message, color: tags.color || '#9147ff' // 无自定义颜色的用户默认用Twitch品牌紫 }; // 把消息推给当前连接的前端 socket.emit("new-chat-message", chatItem); console.log(`${chatItem.username}: ${chatItem.content}`); }); }); // 前端断开连接时关闭tmi客户端,避免资源浪费 socket.on("disconnect", () => { if (twitchClient) twitchClient.disconnect(); }); }); httpServer.listen(port, () => { console.log(`Server is running on ${port}!`); })
3. 改造前端聊天页(twitch-chat.html)
页面加载后先从URL里拿到频道名,连接Socket.IO服务加入对应频道,每收到服务端推的新消息就追加到页面上,实现只读展示即可。
改造后的完整代码:
<!DOCTYPE html> <html> <head> <title>Twitch Chat</title> <style> .chat-container { max-width: 800px; margin: 2rem auto; border: 1px solid #efeff1; border-radius: 8px; height: 70vh; overflow-y: auto; padding: 1rem; font-family: sans-serif; } .chat-item { margin: 0.5rem 0; line-height: 1.5; } .username { font-weight: bold; margin-right: 0.5rem; } footer { text-align: center; margin-top: 2rem; } </style> </head> <body> <div class="chat-container" id="chatContainer"></div> <footer> <p>© Drake Hopkins</p> <p>Built with tmi.js</p> </footer> <!-- 引入Socket.IO客户端脚本,服务端集成后会默认提供该静态资源 --> <script src="/socket.io/socket.io.js"></script> <script> const socket = io(); const chatContainer = document.getElementById('chatContainer'); // 从URL参数里拿到要监听的频道名 const urlParams = new URLSearchParams(window.location.search); const channelName = urlParams.get('channel'); // 连接成功后加入对应频道 socket.on('connect', () => { socket.emit('join-channel', channelName); }); // 收到新聊天消息时渲染到页面 socket.on('new-chat-message', (chatItem) => { const itemDom = document.createElement('div'); itemDom.className = 'chat-item'; itemDom.innerHTML = `<span class="username" style="color: ${chatItem.color}">${chatItem.username}:</span><span class="message">${chatItem.content}</span>`; chatContainer.appendChild(itemDom); // 自动滚动到最新消息 chatContainer.scrollTop = chatContainer.scrollHeight; }); </script> </body> </html>
后续可选优化(新手阶段可暂时跳过)
- 增加tmi客户端缓存,多个用户监听同一个频道时复用同一个连接,减少资源消耗
- 增加Twitch表情、徽章的解析展示逻辑
- 增加异常提示,对频道不存在、连接失败等场景给用户明确反馈
- 给聊天消息增加时间戳展示
内容的提问来源于stack exchange,提问作者Drake
相关产品推荐
相关产品推荐

