前言
此处为一个流式接口的对话,前端需要以流式的方式进行输出。前端框架使用Vue3
说明:此处将展示对话内容的多于元素已删除,只保留了消息体的HTML。
<div v-for="item in data" class="chat-item">
<div :class="'message ' + item.type">
<div class="response-info">
<span class="message-text">{{ item.content }}</span>
</div>
</div>
</div>
定义streamChatData为获取流式数据的方法。在data不断变化的过程中,HTML中的消息就会以流式的方式打印出来。
const streamChatData = async (params) => {
// data为存储对话的数组
data.value.push({
type: 'response',
content: '正在获取答案中...'
})
// 流式接口地址
const url = `${base_chat}/xxxx/chat`
try {
const access_token = Cookies.get('access_token')
// 发送请求
const response = await fetch(url, {
method: 'post',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${access_token}`
},
mode: 'cors',
body: JSON.stringify(params)
})
if(!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`)
}
// console.log(`获取的数据大小${response.headers.get('content-length')/1000/1000}M`)
data.value[data.value.length - 1].content = ''
const reader = response.body?.getReader()
const dispatchRaw = async (raw: string) => {
raw = raw.trim();
const matches = raw
.split("data:")
.map((item: string) => item.trim())
.filter((item: string) => item)
for (let va of matches) {
va = !!va && JSON.parse(va) || {}
await onMessagData(va)
}
}
while(true) {
const { value, done } = await reader?.read()
if(done) {
console.log('stream done')
isResponsed.value = true
break
}
const utf8Decoder = new TextDecoder("utf-8");
let raw: any = value ? utf8Decoder.decode(value, { stream: true }) : ''
console.log('stream raw:', raw)
dispatchRaw(raw)
// isResponsed.value = true
}
} catch(err) {
console.log(err)
}
}
const onMessagData = (va) => {
data.value[data.value.length - 1].content += va?.message || ''
}
扩展
实现将一个字符串以打字机的效果输出,每次只打印一个字。
<span>{{ text }}</span>
// 定义一个完整的字符串
let fullText = ref('Hello Typewriter Effect!')
// HTML中展示的字符串
let text = ref()
// 定义打字机打印的方法
const typeWriter = (index) => {
if(index < fullText.value.length) {
text.value = fullText.value.slice(0, index)
}
setTimeout(() => {
typeWriter(index + 1)
}, 100);
}