共计 1488 个字符,预计需要花费 4 分钟才能阅读完成。
为什么需要流式对话?
在传统的聊天实现中,很多开发者会使用轮询(Polling)方案来获取对话更新。但这种方式存在几个明显缺陷:

- 资源浪费:频繁的 HTTP 请求会产生大量无效查询
- 延迟明显:需要等待完整响应才能显示内容
- 状态复杂:前后端需要维护复杂的同步逻辑
技术选型:SSE vs WebSocket
实现流式通信主要有两种方案:
- Server-Sent Events (SSE)
- 单向通信(服务端→客户端)
- 基于 HTTP 协议,TTFB(首字节时间)通常在 50-300ms
- 自动重连机制
-
兼容性:除 IE 外主流浏览器都支持
-
WebSocket
- 全双工通信
- 需要维护独立连接
- 适合需要双向实时通信的场景
对于聊天机器人这种以接收为主的场景,SSE 是更轻量的选择。
核心实现
状态管理设计
使用 useReducer 管理对话状态:
type Message = {
id: string;
role: 'user' | 'assistant';
content: string;
};
type State = {messages: Message[];
status: 'idle' | 'connecting' | 'streaming' | 'error';
};
type Action =
| {type: 'SEND_MESSAGE'; payload: Message}
| {type: 'APPEND_CHUNK'; payload: string}
| {type: 'SET_STATUS'; payload: State['status'] };
SSE 连接 Hook
创建自定义 Hook 处理 SSE 连接:
function useSSE(url: string, onMessage: (data: string) => void) {useEffect(() => {const eventSource = new EventSource(url);
eventSource.onmessage = (event) => {onMessage(event.data);
};
// 错误处理和重试逻辑
const handleError = () => {eventSource.close();
setTimeout(() => {
// 指数退避重试
useSSE(url, onMessage);
}, 1000 * Math.pow(2, retryCount));
};
return () => {eventSource.close();
};
}, [url]);
}
渐进式渲染优化
为避免频繁重渲染,采用分块更新策略:
- 初始接收时每 200ms 更新一次 DOM
- 当检测到滚动条在底部时,改为实时更新
- 使用 requestAnimationFrame 优化渲染性能
代码规范要点
- 所有组件使用 React.memo 包裹
- 事件处理器使用 useCallback 缓存
- 错误边界组件捕获渲染异常
- 类型定义使用泛型处理分页数据
常见问题解决方案
连接池管理
- 单页面不超过 3 个 SSE 连接
- 使用 AbortController 取消未完成请求
- 页面隐藏时自动断开连接
大文本处理
- 服务端按
\n\n分块 - 前端使用双向链表存储分块
- 超过 10KB 的响应启用虚拟滚动
移动端适配
- 添加 touch 事件支持
- 禁用页面缩放
- 使用 CSS will-change 属性优化渲染
性能验证
通过 Chrome Performance Tab 分析:
- 脚本执行时间控制在 50ms 以内
- 避免强制同步布局
- 内存占用平稳无泄漏
进阶思考
如何实现打字机动画效果?可以考虑:
- 使用 CSS 动画控制每个字符的出现
- 通过 requestAnimationFrame 精确控制渲染节奏
- 结合 Web Animations API 实现平滑过渡
希望这篇指南能帮助你构建流畅的对话体验!在实际项目中,还需要根据具体需求调整更新策略和错误处理机制。
正文完
