markstream-vue 入门:AI 流式 Markdown 渲染
AI 对话的回复是一段一段「流」出来的,普通的 Markdown 渲染器在这种场景下会闪烁、抖动、代码块错乱。markstream-vue 专为这个场景而生。
问题:流式输出为什么难渲染
AI 聊天的返回不是一次给全,而是逐 token 推送——你看到的「打字机效果」。这对渲染是巨大挑战:
半截语法问题。流到一半的 Markdown 是不合法的:代码块只有开头 ``` 没有结尾、表格只流到第二行、数学公式只有一半 $。传统渲染器拿到这种「残缺文本」会渲染错乱,等下一段来了又重渲染,用户看到的就是闪烁和布局跳动。
性能问题。每来一段就要整体重新解析渲染全文,回复长了(几千 token)越到后面每帧的重渲染开销越大,低端设备肉眼可见地卡。
markstream-vue 针对这两点做了专项处理:增量解析(只处理新增的部分)+ 未闭合语法容错(代码块没闭合就先按代码块渲染,等闭合了自然完整)。本站 AI 对话页用的就是它配合 Vue 做流式渲染。
基本用法
pnpm add markstream-vue
<script setup>
import { ref } from 'vue';
import { MarkStream } from 'markstream-vue';
const content = ref('');
// 典型的 SSE 流式请求
async function ask(question) {
const response = await fetch('/api/ai/chat', {
method: 'POST',
body: JSON.stringify({ question }),
});
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
content.value += decoder.decode(value); // 不断追加到同一个字符串
}
}
</script>
<template>
<MarkStream :content="content" />
</template>
接法的心智模型简单到离谱:不管流从哪来(SSE、WebSocket、fetch reader),只要持续往一个字符串 ref 里追加,组件就会把它渲染成稳定的 Markdown。组件自己处理增量与容错。
WebSocket 流的接法
本站 AI 对话走 WebSocket(后端 Spring WebSocket 推流),接法同理:
ws.onmessage = (event) => {
const chunk = JSON.parse(event.data);
if (chunk.type === 'delta') {
content.value += chunk.text; // 追加增量
} else if (chunk.type === 'done') {
loading.value = false; // 流结束
}
};
一个建议:流结束后不要再整段重渲染。有的实现收尾时把最终文本重新 parse 一遍「求干净」,反而引入一次全量重渲染的闪烁——markstream 的增量方式下,最后一个 chunk 渲染完自然就是终态。
与打字机效果的配合
有的产品想在「拿到全部回复」后做匀速打字机(本地模拟流),markstream 也适用:
const full = await fetchFullAnswer();
const timer = setInterval(() => {
content.value = full.slice(0, content.value.length + 2);
if (content.value.length >= full.length) clearInterval(timer);
}, 30);
不过真实流式(真逐 token)的体验优于模拟打字机——用户等待首 token 的时间才是体感关键,能真流式就别模拟。
样式与代码高亮
组件支持自定义渲染样式(代码块、表格、引用等 CSS 类都可覆盖)。代码高亮默认可用,按需配置主题:
<MarkStream :content="content" class="ai-message" />
/* 覆盖代码块风格示例 */
.ai-message pre {
border-radius: 10px;
background: #0d1117;
}
AI 回复里最重的内容就是代码块(AI 特别爱贴代码),代码块渲染的稳定性基本决定了对话区 80% 的观感——这也是选专用流式渲染器而不是普通 markdown-it 的最大理由。
小结
markstream-vue 解决的是「AI 流式输出 × Markdown」这对组合的特殊问题:增量解析保证性能不衰减,未闭合语法容错保证不闪烁。接入成本几乎为零——往一个 ref 里追加文本,剩下的交给它。做 AI 对话界面,这是基础设施级的组件。