这里写自定义目录标题
前言
在开发基于uni-app的应用时,我们常常需要实现一些复杂的交互功能,比如类似文心一言的对话系统。这种对话系统的核心体验之一就是流式输出效果——用户发送问题后,回答内容像打字机一样逐字显示,而不是一次性弹出完整结果。这种效果在H5环境下可以通过fetch API的流式处理实现,但在uni-app中由于环境限制,我们需要采用websocket方案来实现类似效果。
一、为什么不能直接使用fetch实现流式效果?
在标准的H5开发中,我们可以通过fetch API结合ReadableStream API实现流式数据处理:
fetch(url)
.then(response => response.body.getReader())
.then(reader => {
// 逐块读取数据并更新UI
});
但uni-app的运行环境对原生API有所限制,虽然uni-app提供了uni.request作为fetch的替代方案,但它并不支持流式读取响应体。这意味着我们无法像在纯H5中那样直接通过HTTP请求实现逐字节接收数据,从而无法实现流畅的打字机效果。
二、websocket:uni-app中的流式数据解决方案
鉴于HTTP请求的限制,websocket成为了实现实时流式数据传输的理想选择。websocket协议提供全双工通信通道,允许服务器主动向客户端推送数据,非常适合这种需要实时更新UI的场景。
2.1、uni-app中的websocket API概览
uni-app提供了完整的websocket支持,关键API包括:
uni.connectSocket(Object): 初始化websocket连接
uni.onSocketOpen(Object): 监听websocket连接打开事件
uni.onSocketMessage(Object): 监听websocket收到消息事件
uni.sendSocketMessage(Object): 通过websocket发送消息
uni.closeSocket(Object): 关闭websocket连接
2.2、websocket实现流式对话的完整流程
步骤1:建立websocket连接
在用户进入对话页面时,我们需要建立websocket连接:
// 建立连接
uni.connectSocket({
url: 'wss://your-api-domain.com/socket',
header: {
'content-type': 'application/json'
},
success: function(res) {
console.log('连接成功', res);
},
fail: function(err) {
console.error('连接失败', err);
// 这里可以实现重连逻辑
}
});
// 监听连接打开事件
uni.onSocketOpen(function (res) {
console.log('WebSocket连接已打开:' + res);
// 可以在这里发送初始握手消息
});
步骤2:处理收到的消息
当服务器通过websocket推送数据时,我们需要逐帧处理并更新UI:
// 监听websocket消息
let buffer = ''; // 用于缓存未完成的消息
uni.onSocketMessage(function (res) {
const data = res.data;
// 如果是逐帧发送的文本数据
if (typeof data === 'string') {
buffer += data;
// 检查是否是完整消息(根据实际协议判断)
if (data.endsWith('</end>')) { // 假设使用特殊标记表示消息结束
const finalMessage = buffer.replace('</end>', '');
processCompleteMessage(finalMessage);
buffer = '';
} else {
// 更新UI显示部分消息
updateMessageDisplay(buffer);
}
}
});
步骤3:实现打字机效果的UI更新
为了实现流畅的打字机效果,我们需要在UI层做些处理:
function updateMessageDisplay(text) {
// 创建一个临时节点用于文本拆分
const words = text.split('');
// 清空当前消息显示区域
this.setData({
currentMessage: ''
});
// 使用setTimeout逐字显示
let index = 0;
const timer = setInterval(() => {
this.setData({
currentMessage: words.slice(0, index + 1).join('')
});
index++;
if (index >= words.length) {
clearInterval(timer);
}
}, 100); // 调整这里控制打字速度
}
步骤4:发送用户消息
当用户点击发送按钮时,通过websocket发送消息:
// 发送用户消息
uni.sendSocketMessage({
data: JSON.stringify({
type: 'user_message',
content: userInput,
sessionId: this.sessionId
}),
success: function() {
console.log('消息发送成功');
},
fail: function(err) {
console.error('消息发送失败', err);
}
});
步骤5:优雅地关闭连接
在页面卸载或对话结束时,及时关闭websocket连接:
uni.closeSocket({
success: function(res) {
console.log('连接已关闭', res);
}
});
三、完整代码示例
// 对话页面的生命周期方法
onLoad() {
this.initWebSocket();
},
initWebSocket() {
// 建立连接
uni.connectSocket({
url: 'wss://your-api-domain.com/socket',
header: {
'content-type': 'application/json'
}
});
// 监听连接打开
uni.onSocketOpen(() => {
console.log('WebSocket连接已打开');
// 发送身份认证等初始化消息
uni.sendSocketMessage({
data: JSON.stringify({
type: 'auth',
token: this.userToken
})
});
});
// 监听消息
let buffer = '';
uni.onSocketMessage((res) => {
const data = res.data;
if (typeof data === 'string') {
buffer += data;
if (data.endsWith('</end>')) {
const finalMessage = buffer.replace('</end>', '');
this.processCompleteMessage(finalMessage);
buffer = '';
} else {
this.updateMessageDisplay(buffer);
}
}
});
// 监听错误
uni.onSocketError((err) => {
console.error('WebSocket发生错误:', err);
// 实现重连逻辑
setTimeout(() => this.initWebSocket(), 5000);
});
},
processCompleteMessage(message) {
// 处理完整消息,如添加到消息列表
this.messages.push({
type: 'ai',
content: message
});
this.scrollToBottom();
},
updateMessageDisplay(text) {
const words = text.split('');
this.setData({
currentTypingMessage: ''
});
let index = 0;
const timer = setInterval(() => {
this.setData({
currentTypingMessage: words.slice(0, index + 1).join('')
});
index++;
if (index >= words.length) {
clearInterval(timer);
}
}, 100);
},
// 用户发送消息
sendMessage() {
const userInput = this.inputText.trim();
if (!userInput) return;
this.messages.push({
type: 'user',
content: userInput
});
uni.sendSocketMessage({
data: JSON.stringify({
type: 'user_message',
content: userInput,
sessionId: this.sessionId
})
});
this.inputText = '';
this.scrollToBottom();
},
在uni-app中实现类似文心一言的流式对话功能,虽然不能直接使用H5的fetch流式API,但通过websocket可以实现类似甚至更好的效果。完整的实现需要处理好以下几个关键点:
- websocket连接的生命周期管理
- 消息的分片接收与拼接
- UI层的打字机效果实现
- 异常处理与重连机制
- 通过本文提供的方法和代码示例,您应该能够在uni-app项目中顺利实现流畅的流式对话体验,为用户提供不同的交互感受。