流式输出
1.发起流式请求 (ChatInput.vue - fetchMessage 函数)
typescript
const response = await fetch("/api/chat/stream", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({
message,
session_id: sessionId,
}),
});使用原生
fetchAPI 而不是 Axios(因为需要读取流)向后端
/api/chat/stream接口发送 POST 请求携带用户消息和会话 ID
2.获取可读流
typescript
const reader = response.body?.getReader();
const decoder = new TextDecoder();通过
response.body.getReader()获取一个可读流阅读器创建
TextDecoder用于将二进制数据解码为文本
3.初始化 AI 消息占位符
typescript
messageStore.addAIMessage(""); // 添加一个空的 AI 消息
aiMessageIndex = messageStore.message.length - 1; // 记录该消息的索引4.循环读取流数据
typescript
while (true) {
const { done, value } = await reader.read();
if (done) break; // 流结束时退出循环
const text = decoder.decode(value, { stream: true });
buffer += text;
// 按换行符分割处理
const lines = buffer.split("\n"); //分割成数组
buffer = lines.pop() || ""; // 保留不完整的最后一行,pop()删除数组最后一个元素,并且返回他
for (let line of lines) {
if (line.startsWith("data: ")) {
const jsonStr = line.slice(6); //字符串方法,从字符串索引6开始切割到末尾
const data = JSON.parse(jsonStr); //jsonStr是{"content": "..."}这样的结构
if (data.content) {
fullContent += data.content;
messageStore.updateAIMessageContent(aiMessageIndex, fullContent);// 更新状态仓库数组
}
}
}
}5.解析 SSE 格式数据
前端解析步骤:
检测行是否以
data:开头提取
data:后面的 JSON 字符串解析 JSON 获取 content 字段
累加到
fullContent变量中
6.视图渲染
vue
<div v-else class="m-6 px-4 py-6">
<div class="max-w-4xl mx-auto">
<AIMarkdown :content="content" />
</div>
</div>7.释放资源
typescript
finally {
reader.releaseLock(); // 释放阅读器锁
}流式输出
http://localhost:8090/archives/liu-shi-shu-chu