输出效果
基础知识
Server-Sent Events (SSE)
SSE 是一种允许服务器主动向客户端发送数据的技术。与 WebSocket 相比,SSE 是单向的——只有服务器可以发送数据。
Express 框架
Express 是一个灵活、轻量的 Node.js web 应用框架,广泛用于快速开发 web 应用。
EventSource API
EventSource API 是浏览器中的一个接口,用于接收来自服务器的事件流。
设置项目
安装 Node.js 和 Express
确保你的系统已安装 Node.js。通过运行 npm init
创建一个新项目,并使用 npm install express
安装 Express。客户端文件我们使用 ejs
模板,所以也请安装 npm install ejs
项目结构
chat-dialog/
│
└── views/
└── home.ejs
│
├── main.js
├── package.json
在本文,我们先使用 Express 创建一个 Node.js 服务端,定义了一个 /events
SSE路由来发送事件流,这里这里模拟发送一段文本,每隔 50ms 发送一个字符。
创建服务端(main.js)
// main.js
const express = require("express")
const path = require('path')
const app = express()
// 配置视图引擎和视图的路径
app.set("view engine", "ejs")
app.set("views", path.join(__dirname, "views"))
// 客户端模拟输出的内容
const str = `在 JavaScript 中,Math.max.apply(null, [1, 2, 5, 3, 4]) 这行代码用于找出数组中的最大值。这里使用了 Function.prototype.apply() 方法来调用 Math.max 函数。让我们分解这行代码来理解它是如何工作的:Math.max:这是 JavaScript 中的一个函数,用于返回一组数字中的最大值。通常,它的用法是 Math.max(num1, num2, ..., numN),其中 num1, num2, ..., numN 是需要比较的数字。.apply() 方法:这是 JavaScript 函数对象的一个方法,它允许你调用一个函数,并为它指定 this 值和参数数组。.apply() 的第一个参数是用来设置 this 的值的,第二个参数是一个数组,数组中的元素将被作为单独的参数传递给函数。在 Math.max.apply(null, [1, 2, 5, 3, 4]) 这个表达式中:Math.max 作为函数被调用。null 作为第一个参数传递给 .apply(),在这个特定的情况下,this 的值不重要,因为 Math.max 是一个静态方法,不依赖于 this 的值。因此,传递 null 是安全的。[1, 2, 5, 3, 4] 作为第二个参数,是一个数组,其元素将被作为单独的参数传递给 Math.max。结果,Math.max.apply(null, [1, 2, 5, 3, 4]) 相当于调用 Math.max(1, 2, 5, 3, 4),返回这些数字中的最大值,即 5。`
// SSE 路由
app.get("/events", (req, res) => {
res.setHeader("Content-Type", "text/event-stream")
res.setHeader("Cache-Control", "no-cache")
res.setHeader("Connection", "keep-alive")
res.flushHeaders()
let count = 0
const intervalId = setInterval(() => {
if (count < str.length) {
res.write(`data: ${str[count++]}\n\n`)
} else {
clearInterval(intervalId)
res.end()
}
}, 50)
// 处理关闭连接
req.on("close", () => {
clearInterval(intervalId)
})
})
// 首页路由
app.get("/", (req, res) => {
res.render("home")
})
// 监听端口
app.listen(8685, () => {
console.log("服务已启动,监听端口8685")
})
创建客户端(views/home.ejs)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>chatGPT 逐字输出</title>
</head>
<body>
<h1>ChatGPT 模拟输出</h1>
<div id="output"></div>
<script>
const outputElement = document.getElementById('output')
const eventSource = new EventSource('/events')
eventSource.onmessage = function(event) {
outputElement.textContent += event.data
// 此处硬编码,模拟输出完毕后关闭SSE,不然会一直无限请求输出
if (outputElement.textContent.length === 687) {
eventSource.close()
}
}
eventSource.onerror = function() {
console.error('发生错误,SSE 连接关闭')
eventSource.close()
}
</script>
</body>
</html>
项目依赖(package.json)
{
"name": "chat-dialog",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "瓶子",
"license": "ISC",
"dependencies": {
"ejs": "^3.1.9",
"express": "^4.18.2"
}
}
运行和测试
启动 Express 服务器,并在浏览器中访问 http://localhost:8685
查看实时输出效果。
结语
你已经了解了如何使用 SSE 和 Express 来实现实时数据传输和逐字显示效果。这只是 SSE 和实时通信的冰山一角,你可以进一步探索和扩展这些技术,创造更多有趣的功能。