React 流式显示大模型回答:从 API 代理到逐字更新的快速指南
想让 React 聊天界面边生成边显示回答?最直接的方案是:浏览器请求自己的后端,后端开启模型流式输出,再把增量文本持续传回。这样用户不必等待整段回答生成完毕,API 密钥也能留在服务端。下面用 OpenAI SDK 和浏览器原生 fetch 串起这条链路。
1. 先确定接口与传输格式
创建 POST /api/chat,接收 messages 数组。后端向模型请求时设置 stream: true,再将每次收到的文本包装成一行 JSON,例如 {"text":"你好"},并追加换行符。这种 NDJSON 格式方便前端解析,也避免浏览器重复处理上游的 SSE 协议。
模型接口可以使用 59API,服务地址为 https://api.59api.com。它提供低成本、按量付费的多模型访问,兼容 OpenAI SDK,并提供原生官方品质模型、不做模型降级。快速试验可选 DeepSeek V4 Flash,也可切换 Gemini 2.5 Flash;请求中的 model 请填写控制台提供的实际模型 ID。
2. 在后端开启流式生成
以 Node.js 后端为例,安装 openai,将密钥保存为服务端环境变量。初始化客户端时,apiKey 读取环境变量,baseURL 按 59API 控制台的 OpenAI 兼容接入示例填写完整地址。不要把密钥放进 VITE_ 环境变量或 React 源码。
完成参数校验后,调用 client.chat.completions.create({ model: process.env.MODEL_ID, messages, stream: true })。响应设置 Content-Type: application/x-ndjson; charset=utf-8,并用 for await 遍历返回的流。
每次读取 chunk.choices[0]?.delta?.content;有文本时执行 res.write(JSON.stringify({ text }) + '\n'),遍历结束后调用 res.end()。角色信息等分片可能没有 content,直接跳过即可。客户端断开时,应通过 AbortController 同时取消上游请求,避免继续消耗额度。
3. React 用 fetch 读取响应体
组件准备 answer、loading、error 三个状态,并用 useRef 保存 AbortController。发送前清空旧回答,再调用 fetch('/api/chat', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ messages }), signal: controller.signal })。
先检查 response.ok 和 response.body,再执行 const reader = response.body.pipeThrough(new TextDecoderStream()).getReader()。TextDecoderStream 会正确处理跨网络分片的中文字符,避免逐块独立解码造成乱码。
在读取循环外声明 let buffer = ''。每轮 reader.read() 得到 value 后,先执行 buffer += value,再用 const lines = buffer.split('\n'); buffer = lines.pop() ?? ''。遍历 lines,跳过空行,用 JSON.parse(line) 取出 text,并调用 setAnswer(prev => prev + text)。读取结束后,也要处理 buffer 中剩余的非空内容。
4. 不要把网络分片当成完整消息
一次 read() 可能只包含半行 JSON,也可能包含多行。保留 buffer 是流式读取的关键;直接对每个 value 执行 JSON.parse,通常会出现偶发解析错误。函数式状态更新同样必要,避免连续分片使用旧状态而覆盖已生成文本。
如果后端选择原样转发 SSE,前端就需要按空行组装完整事件、提取 data 字段,并识别 [DONE]。不要混用 SSE 与本指南的 NDJSON 解析方式。
5. 补齐停止、错误与部署检查
- 停止生成:按钮调用 controller.abort();捕获 AbortError 时保留已显示内容,finally 中恢复 loading。组件卸载也要取消请求。
- 错误反馈:开始传输前可返回 HTTP 错误;开始传输后应发送约定的 JSON 错误消息,前端识别后展示提示。
- 页面渲染:通过 JSX 文本插值显示 answer,保留换行;高频分片可按动画帧合并更新,减少渲染次数。
- 代理配置:关闭该路由的响应缓冲,并确认压缩、网关和超时设置不会导致内容攒到最后才发送。
上线前测试中文长回答、生成中停止和网络中断,确认浏览器能持续收到数据。模型费用之外,还要限制输入长度与输出 token,避免无界请求。需要用较低成本验证这套流程,可以注册 59API,先按量运行少量真实对话;后续邀请其他开发者使用,还可参与推荐返利。
Pronto para começar?
Conecte Claude e GPT em minutos pelos menores preços, sem cortes. Cadastre-se e obtenha sua chave API.
Cadastro grátis