背景
让Cursor生成小程序中大模型调用内容回复的流式输出时一直有问题,参考整理此文章。
参考原文:https://blog.csdn.net/weixin_47684422/article/details/145859543
一、什么是流式传输?
流式传输(Streaming)指的是将数据分为若干段(chunk),边生成边发送,客户端则边接收边处理。常见于视频播放、音频播放、AI对话等场景。其优点包括:
- 降低延迟,提升响应速度
- 支持大数据量分段处理
- 改善用户体验(如AI逐字回复)
二、微信小程序中的流式传输方案
1. 微信小程序的限制
- 无fetch:小程序没有浏览器的
fetch
接口,不能直接用Response.body.getReader()
流式读取。 - WebSocket不适用于所有后端:部分AI接口只支持HTTP流,不支持WebSocket。
2. 官方能力:onChunkReceived
微信小程序自基础库2.23.0起,wx.request
新增了enableChunked
参数和onChunkReceived
回调,支持HTTP分块(chunked)传输。
要点:
enableChunked: true
开启流式传输- 监听
onChunkReceived
获取每一块数据 - 需自行拼接与处理数据内容
三、代码实现
1. 发起流式请求
const requestTask = wx.request({
url: `${config.baseUrl}/ai/deepSeek`,
header: {
"X-Access-Token": wx.getStorageSync("token"),
},
data: JSON.stringify({
question: this.question,
appId: config.APP_ID,
userName: 'xxx'
}),
method: "POST",
enableChunked: true, // 开启流式传输
responseType: "arraybuffer", // 推荐使用arraybuffer,兼容性更好
});
// 监听分块数据
if (requestTask?.onChunkReceived) {
requestTask.onChunkReceived(this.handleChunkData);
} else {
// 不支持流式,降级为普通请求
this.normalRequest();
}
2. 分块数据处理
handleChunkData(res) {
try {
// 微信小程序ArrayBuffer转字符串
let rawStr = Array.from(new Uint8Array(res.data))
.map(byte => String.fromCharCode(byte))
.join("");
// 处理中文乱码
rawStr = unescape(encodeURIComponent(rawStr));
// 提取JSON内容
const chunkJson = rawStr.split("data: ")[1];
const rawData = JSON.parse(chunkJson);
// 提取AI回复内容
const text = rawData.choices?.[0]?.delta?.content;
// 过滤无用内容(如<think>标签)
if (text === "</think>") {
this.isThink = false;
}
if (!this.isThink) {
const tx = text.replace("</think>", "");
this.streamBuffer += tx;
// 实时渲染(可用markdown-it等库转换)
const answer = this.list[this.list.length - 1];
answer.content = markdown.render(this.streamBuffer);
// 节流更新页面
this.throttleUpdate();
}
} catch (e) {
console.error("数据处理异常:", e);
}
},
3. 辅助方法
// 节流更新,防止频繁渲染影响性能
throttleUpdate() {
if (!this.updateTimer) {
this.updateTimer = setTimeout(() => {
this.$forceUpdate();
this.scrollToBottom();
this.updateTimer = null;
}, 500);
}
}
四、注意事项与优化建议
- 字段结束标识:小程序没有
onReceivedEnd
事件,无法直接判断流式内容是否结束。建议后端返回特殊标识(如[DONE]
)或协议字段,前端据此判断。 - 中文兼容性:ArrayBuffer转字符串时,注意处理中文编码问题,推荐用
TextDecoder
或unescape(encodeURIComponent(...))
方法。 - 性能优化:流式内容更新频繁,建议节流更新页面,避免卡顿。
- uniapp注意:如用uniapp,必须用
wx.request
,uni.request
不支持chunked流。 - 内容过滤:部分大模型(如deepSeek)返回的内容含有think标签等无用数据,需按业务过滤。