打造一个支持流式输出的 Vue Markdown 渲染组件
在构建类似 ChatGPT 的对话交互界面时,我发现 Vue 社区缺乏一个原生支持流式 Markdown 渲染 的方案。而在 React 社区,很多人会使用 react-markdown
来实现类似需求。
react-markdown
的底层原理其实不复杂:它将 Markdown 文本解析为 markdown AST,再转换为 HTML AST,最后通过 React 的虚拟 DOM 机制将其渲染为 React 元素。这种方式充分利用了 React 的 Reconciliation 特性,在性能上表现非常优秀。
受此启发,我参考其实现方式,开发了一个 Vue 版本的流式 Markdown 渲染组件。
- 👉 项目地址:vue-markdown-renderer
- 👉 在线预览:live demo
大概功能
- 流式输出支持:模拟大语言模型逐字输出的效果,支持按粒度拆分内容逐步渲染
- 高亮美观 :集成 shiki 实现语法高亮,且也支持流式处理
- 性能优异 :基于 Vue 的虚拟 DOM 实现渐进渲染,性能显著优于直接使用
v-html
或markdown-it
安装方式
bash
npm install vue-mdr
快速上手
1. 根据拆分的chunk,添加自定义的动画
css
/* animation.css */
.vue-markdown-wrapper > *,
.vue-markdown-wrapper .text-segmenter,
.vue-markdown-wrapper .shiki-stream span {
animation: fade-in 0.5s ease-in-out;
}
@keyframes fade-in {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
2. 使用示例
vue
<script setup>
import { VueMarkdownRenderer } from "vue-mdr";
import { ref, onMounted } from "vue";
import "./animation.css";
// 创建模拟流式输出的函数
function createStream(text, chunkSize = 10, delay = 50) {
let position = 0;
return new ReadableStream({
async pull(controller) {
if (position >= text.length) {
controller.close();
return;
}
const chunk = text.slice(position, position + chunkSize);
position += chunkSize;
controller.enqueue(chunk);
await new Promise((r) => setTimeout(r, delay));
},
});
}
const mdText = ref("");
const isRender = ref(true);
async function clickHandle() {
mdText.value = "";
isRender.value = true;
const res = await fetch("./md.md");
const md = await res.text();
const stream = createStream(md);
for await (const chunk of stream) {
mdText.value += chunk;
}
isRender.value = false;
}
onMounted(clickHandle);
</script>
<template>
<div>
<button @click="clickHandle" :disabled="isRender">Re-generate ~</button>
<article class="vue-markdown-wrapper">
<VueMarkdownRenderer :md="mdText" />
</article>
</div>
</template>
为什么不用现成的库?
markdown-it
等常用库 不支持流式渲染- 现有方案缺乏良好的 动画与高亮支持
- 大多数 Markdown 渲染方式依赖
v-html
,性能和可控性都较差
因此我将 Vue、Shiki 和流式渲染机制组合,封装出这个高效、美观、易用的 Markdown 渲染组件。开发者只需几行代码,就能轻松实现类似 AI 对话的「边输出边渲染 + 高亮 + 动画」效果。
项目结构简介
VueMarkdownRenderer.ts
:组件主入口,核心的响应式渲染逻辑segmentText.ts
:文本拆分与动画粒度处理模块
最后
虽然 vue-mdr
的实现并不复杂,但它显著提升了我的开发效率和终端用户体验。如果你正在做 AI 聊天、内容生成、知识库问答等应用,这个组件或许正好适合你!