<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>AI 流式输出 + Markdown渲染</title>
<style>
body { max-width: 800px; margin: 20px auto; padding: 0 20px; }
#result {
white-space: pre-wrap;
border: 1px solid #eee;
padding: 16px;
min-height: 200px;
margin-top: 20px;
line-height: 1.6;
}
#result h1, #result h2, #result h3 { margin: 10px 0; }
#result strong { color: #007bff; }
#result code { background: #f4f4f4; padding: 2px 4px; border-radius: 4px; }
#result pre { background: #f4f4f4; padding: 10px; overflow-x: auto; }
#btn { padding: 10px 20px; font-size: 16px; cursor: pointer; }
</style>
</head>
<body>
<h3>AI 流式输出演示(渲染Markdown)</h3>
<button id="btn">开始提问:介绍一下JavaScript</button>
<div id="result"></div>
<!-- 👇 加这一行:引入 Markdown 渲染库(和豆包用的一样) -->
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<script>
const btn = document.getElementById('btn');
const result = document.getElementById('result');
// 👇 用来存完整的回答文本
let fullText = '';
btn.onclick = async () => {
btn.disabled = true;
btn.innerText = 'AI 正在流式输出...';
result.innerText = '';
fullText = '';
try {
const response = await fetch("https://open.bigmodel.cn/api/paas/v4/chat/completions", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "96f0813aca214bb486892a55f7148622.oQFhjTVnwDHvmnEC",
},
body: JSON.stringify({
model: "glm-4-flash",
messages: [{ role: "user", content: "介绍一下JavaScript" }],
stream: true
})
});
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value, { stream: true });
const lines = chunk.split("\n").filter(i => i);
for (let line of lines) {
if (line.startsWith("data: ")) {
const jsonStr = line.replace("data: ", "");
if (jsonStr === "[DONE]") continue;
try {
const data = JSON.parse(jsonStr);
const text = data.choices[0]?.delta?.content || "";
// 👇 拼接完整文本
fullText += text;
// 👇 关键:把 Markdown 渲染成 HTML(豆包就是这么做的!)
result.innerHTML = marked.parse(fullText);
} catch (e) {}
}
}
}
} catch (err) {
result.innerText = "错误:" + err.message;
} finally {
btn.innerText = "重新提问";
btn.disabled = false;
}
};
</script>
</body>
</html>
总结:
-
用
fetch发请求 →stream: true -
用
reader.read()接收二进制流 -
转字符串 → 按行拆分
-
取
data:后面的 JSON -
取
choices[0].delta.content拼文字 → 渲染页面
备注:
Markdown:带「排版标记」的纯文本字符串
直接复制到.html,然后到open.bigmodel.cn/apikey/plat… 平台拿取一个api的key值
这段代码,就是豆包 /chat/completion 接口的工作方式 + 渲染方式!