完整代码包含很多定制代码,不贴出来了,只放关键代码
1、使用fetch实现流式输出,并拼接为一个字符串展示
try {
const response = await fetch('https://xxxx', {//你的Api地址
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(你的参数),
signal: abortController.signal, // 使用 signal 来控制请求
});
if (!response.ok) {
throw new Error('Network response was not ok');
}
const reader = response.body?.getReader();
const decoder = new TextDecoder('utf-8');
let accumulatedContent = '';
if (reader) {
while (true) {
const { done, value } = await reader.read();
if (done) {
//此处代表输出完毕可做自定义操作
break;
}
const chunk = decoder.decode(value, { stream: true });
const parsedChunk = JSON.parse(chunk);
accumulatedContent += parsedChunk.message.content;//最终拼接成一个完整字符串
scrollToBottom();
}
}
} catch (error: any) {//用于终止流式输出时抛出异常
if (error.name === 'AbortError') {
console.log('Fetch aborted:', error);
} else {
console.error('Fetch error:', error);
}
}
2、终止流式输出,当流式输出没结束时,可手动触发事件暂停输出
//终止回答
const abortController = new AbortController();
const stopRequest = () => {
abortController.abort()
};
3、窗口自动滚动到底部,在聊天记录的窗口上绑定一个ref:messagesRef
const scrollToBottom = () => {
nextTick(() => {
const messagesContainer = messagesRef.value;
if (messagesContainer) {
messagesContainer.scrollTop = messagesContainer.scrollHeight;
}
});
};
4、markdown格式展示和代码高亮
安装
npm install markdown-it
npm install highlight.js
使用
//页面展示
<div v-html="md.render(最终流式输出拼接成的字符串)"></div>
//js引入
import hljs from 'highlight.js';
import javascript from 'highlight.js/lib/languages/javascript';
import 'highlight.js/styles/ir-black.css';
import MarkdownIt from 'markdown-it';
hljs.registerLanguage('javascript', javascript);
let md: MarkdownIt = MarkdownIt({
highlight: function (str: string, lang: string) {
const language = hljs.getLanguage(lang);
if (language) {
try {
return `<div>
<div>
<span>${lang}</span>
</div>
<div class="hljs">
<code>${hljs.highlight(lang, str, true).value}</code>
</div>
</div>`;
} catch (error) {
console.error(error);
}
}
return `<div class="hl-code">
<div>
<span>${lang}</span>
</div>
<div class="hljs">
<code>${md.utils.escapeHtml(str)}</code>
</div>
</div>`;
},
});
5、流式输出如果一次输出多个json对象,则不能像上文一样直接转换,做如下处理
//解析json(由于流式输出可能一次输出多个json,此处需要额外处理)
const extractJsonObjects = (str: string): any[] => {
const jsonObjects: any[] = [];
let depth = 0;
let start = 0;
for (let i = 0; i < str.length; i++) {
switch (str[i]) {
case '{':
if (depth === 0) start = i;
depth++;
break;
case '}':
depth--;
if (depth === 0) {
const jsonStr = str.substring(start, i + 1).trim();
try {
const parsedJson = JSON.parse(jsonStr);
jsonObjects.push(parsedJson);
} catch (e) {
console.error('Invalid JSON:', jsonStr);
}
}
break;
}
}
return jsonObjects;
};
然后在上文fetch部分做如下修改
const chunk = decoder.decode(value, { stream: true });
const jsonObjects = extractJsonObjects(chunk);
for (const jsonObject of jsonObjects) {
if (jsonObject.message && jsonObject.message.content) {
const messageContent = jsonObject.message.content;
accumulatedContent += messageContent;
messagesParams.value.messages[messagesParams.value.messages.length - 1].content = accumulatedContent;
scrollToBottom();
}
}