我对前端不是很熟,然后chagpt玩的多,所以这两天想写个chagpt网页练练手,熟悉一下前端。可是我在实现消息的渲染遇到了问题。一开始,我没有使用流式,然后代码使用Prism、文本使用marked直接渲染还是很方便的。可是当我使用流式以后,发现两者渲染都需要将整段文本段落放入才行,而且代码也是拼凑在一行失去了排版。我看到官网对于文本会先显示markdown标识然后渲染成html,但是排版都是如何实现的,我也是没有很好的思路,目前就是将后端传过来的数据分成三份:第一段文本、代码、后面的文本,html+css+js,很粗糙,就放请求部分吧。有没有大佬指点 。
// 发起请求
async function postMessageAndHandleResponse(url, formData) {
const resp = await fetch(url, {
method: 'POST',
body: formData
});
const reader = resp.body.getReader();
const decoder = new TextDecoder('utf-8');
let accumulatedText = '';
let a = '';
let c = '';
let pos;
let isCodeBlock = false;
let codeCount = 0;
let codeType = "";
const replyElement = document.createElement("div");
const codeElement = document.createElement("div");
const replyBakElement = document.createElement("div");
while(true) {
const { done, value } = await reader.read();
if(done){
console.log('a:', a);
console.log('c:', c);
// replyElement.innerHTML = marked.parse(a);
// chatBox.appendChild(replyElement);
// const providedByText = document.createElement("div");
// providedByText.innerHTML = 'provided by <span class="highlight">mhchat</span>';
// providedByText.classList.add("provided-by");
// replyElement.appendChild(providedByText);
// chatBox.scrollTop = chatBox.scrollHeight;
break;
}
const textChunk = decoder.decode(value);
//console.log('textChunk:', textChunk);
accumulatedText += textChunk;
// console.log('accumulatedText:', JSON.stringify(accumulatedText));
// console.log('Index of "\\n\\n" in accumulatedText:', accumulatedText.indexOf("\n\n"));
// 处理可能的多个消息
// 每个消息以两个换行符结尾
while ((pos = accumulatedText.indexOf("\n\n")) !== -1) {
const message = accumulatedText.substring(0, pos).trim();
accumulatedText = accumulatedText.substring(pos + 2);
console.log('message:', message);
//代码块开始
if (message === '```' || message === '``') {
codeCount += 1;
isCodeBlock = !isCodeBlock;
console.log("isCode Change:" + isCodeBlock + ",count:" + codeCount);
continue;
//代码块结束
}else if(message === '`' && codeCount == 3) {
console.log("代码块结束 isCode Change:" + isCodeBlock);
codeElement.classList.add("message", isCodeBlock ? "code-container" : "received");
const codeBlock = document.createElement("code");
if(codeCount === 3) {
codeCount += 1;
console.log('code Type message2:', codeType);
codeBlock.className = `language-${codeType}`;
}
// 设置代码文本
codeBlock.textContent = c;
const preElement = document.createElement("pre");
preElement.appendChild(codeBlock);
codeElement.appendChild(preElement);
// 在这里调用Prism.highlightElement处理代码高亮
Prism.highlightElement(codeBlock);
// 将处理完成的replyElement添加到消息框
chatBox.appendChild(codeElement);
}else if(codeCount === 1) {
codeCount += 1;
console.log('code Type message1:', message);
codeType = message;
}
replyElement.classList.add("message", isCodeBlock ? "code-container" : "received");
if (isCodeBlock) {
c += message;
// const codeBlock = document.createElement("code");
// if(codeCount === 2) {
// codeCount += 1;
// console.log('code Type message2:', codeType);
// codeBlock.className = `language-${codeType}`;
// }
// codeBlock.textContent += message;
// const preElement = document.createElement("pre");
// preElement.appendChild(codeBlock);
// replyElement.appendChild(preElement);
// // 在这里调用Prism.highlightElement
// Prism.highlightElement(codeBlock);
} else {
if(codeCount === 4) {
replyBakElement.classList.add("message", isCodeBlock ? "code-container" : "received");
replyBakElement.textContent += message;
chatBox.appendChild(replyBakElement);
}else {
a += message;
// 如果是普通文本或图片链接,直接设置innerHTML
//replyElement.innerHTML += marked.parse(message);
replyElement.textContent = a;
chatBox.appendChild(replyElement);
}
}
chatBox.scrollTop = chatBox.scrollHeight;
}
}
}