JavaScript SSE 实现 AI 对话“打字机”效果,核心是服务端按 token/语义片段流式推送(如 data: {"text": "今"}),前端用 EventSource 接收后通过 requestAnimationFrame 逐字渲染,并处理加载、错误、暂停、光标等体验细节。

用 JavaScript 的 SSE(Server-Sent Events)实现 AI 对话的“打字机”流式输出,核心是:前端通过 EventSource 接收服务端逐字/逐 token 推送的数据,再用定时器或原生节奏模拟打字效果。关键不在 SSE 本身(它只负责传输),而在于服务端如何分块推送、前端如何解析 + 渲染 + 控制节奏。
服务端要按 token 或语义片段推送(不是整句)
SSE 流式输出的前提是后端不等模型生成完才发响应,而是边生成边推。例如用 Llama.cpp、Ollama、FastAPI + OpenAI 兼容接口时,需启用 stream=True,并把模型输出的每个 token 或标点后的小片段(如逗号、句号、换行)作为独立事件发送:
- 每条消息格式建议为:
data: {"text": "今"}、data: {"text": "天"}、data: {"text": "天"}…… - 避免发大块文本(如整句),否则前端无法“打字”;也不要用
data:后跟空行分隔,而应确保每次data:行后紧跟换行,且末尾有双换行符(\n\n) - 注意设置响应头:
Content-Type: text/event-stream、Cache-Control: no-cache、Connection: keep-alive
前端用 EventSource 接收并累积内容
创建 EventSource 实例监听 message 事件,把每次收到的 text 追加到一个字符串或数组中,供后续渲染:
- 不要直接 innerHTML 覆盖,而用
textContent或innerText追加,避免 XSS 和格式错乱 - 可先缓存所有片段到
let buffer = [],再统一处理(比如过滤控制字符、合并空格) - 监听
error事件,断连时自动重试(eventSource.close()后新建实例),并提示用户
用 requestAnimationFrame 或 setTimeout 做“打字机”动画
真正实现视觉上的逐字显示,靠的是在接收数据后不立即渲染全部,而是按固定延迟(如 20–50ms)逐个插入字符:
立即学习“Java免费学习笔记(深入)”;
- 推荐用
requestAnimationFrame驱动,更平滑且节电;也可用setTimeout配合递归 - 示例逻辑:收到完整 buffer 后,启动一个渲染函数,每次取 buffer[0] 插入 DOM,然后
buffer.shift(),再调度下一次 - 支持暂停/恢复:用布尔标志位控制是否继续调度;用户滚动到底部时自动追加新内容,保持可见
- 可加光标效果:在输出末尾加一个
<span class="cursor">|</span>,CSS 设animation: blink 1s infinite
兼顾体验细节:加载态、错误、中断与补全
真实场景中不能只管“打字”,还要处理边界情况:
- 发送请求前显示 “AI 正在思考…” 占位符;SSE 连接建立后隐藏,开始流式输出
- 若某次收到
data: {"done": true},说明流结束,可移除光标、触发后续操作(如启用输入框) - 用户中途取消请求?前端调
eventSource.close(),后端也应监听连接断开并终止推理(如检查req.aborted) - 网络抖动导致漏字?可在 message 中附带序号字段
"seq": 123,前端校验连续性,缺字则请求补发或降级为整段渲染


















