如何自己构建一个Markdown增量渲染器
揭秘 Vue3 增量 Markdown 解析组件的神奇魔法
先上效果 演示demo
背景
相信很多大模型前端开发的小伙伴都已经处理过markdown实时解析翻译成html了,传统的方式类似使用Marked、markdown-it等组件全量渲染。但是全量渲染及其消耗性能,会造成大量的重排、重绘,导致页面抖动。
各位前端小伙伴们,今天我要给大家分享一个我最近开发的「Vue3 增量 Markdown 解析组件」。这个组件就像是一个「超级翻译官」,能把枯燥的 Markdown 文本瞬间变成生动的 HTML 页面,而且还支持数学公式、代码高亮等高级功能!废话不多说,让我们一起深入这个组件的「内部世界」吧!
开箱即用模式
# 安装命令
npm install v3-markdown-stream
# 或
yarn add v3-markdown-stream
组件使用示例
<template>
<div>
<MarkdownRender :markInfo="markdownContent" />
</div>
</template>
<script setup>
import { ref } from 'vue'
import { MarkdownRender } from 'v3-markdown-stream';
import 'v3-markdown-stream/dist/v3-markdown-stream.css';
// 静态内容
const markdownContent = ref('# Hello World\n\nThis is a simple markdown example.')
</script>
组件概览
首先,让我们来看看这个组件的「身份证」(都是站在各位巨人的肩膀上)
import { h, defineComponent, computed } from "vue";
import { Fragment, jsxs, jsx } from "vue/jsx-runtime";
import { toJsxRuntime } from "hast-util-to-jsx-runtime";
import remarkParse from "remark-parse";
import rehypeKatex from "rehype-katex";
import "katex/dist/katex.min.css";
import 'highlight.js/styles/github-dark.css';
import remarkMath from "remark-math";
import remarkRehype from "remark-rehype";
import rehypeRaw from 'rehype-raw';
import rehypeHighlight from 'rehype-highlight'
import remarkFlexibleContainers from 'remark-flexible-containers'
import remarkGfm from "remark-gfm";
import { VFile } from "vfile";
import { unified } from "unified";
// 定义组件
export default defineComponent({
name: 'VueMarkdownStreamRender',
props: {
markstr: {
type: String,
required: true,
default: ''
}
},
// 其他实现...
})
这个组件就像是一个「瑞士军刀」,集成了多种功能,让 Markdown 解析变得异常强大!
核心功能包解析 - 武林高手们的各司其职
1. Vue 核心团队
- vue : 提供 h , defineComponent , computed 等核心 API,是整个组件的「骨架」
- vue/jsx-runtime : 提供 Fragment , jsxs , jsx ,让我们可以在 Vue 中优雅地使用 JSX 语法,相当于给 Vue 「装上了 React 的小翅膀」
2. Unified 解析系统 - 解析界的「中央司令部」
- unified : 这是整个解析系统的「大脑」,负责协调各个插件的工作。想象一下,它就像是一个「指挥官」,指挥着一群「小兵」(插件)协同作战
- vfile : 提供文件处理功能,把 Markdown 字符串转换成统一的文件格式,相当于给文本「穿上了标准化的衣服」
3. Remark 家族 - Markdown 的「魔法师」
- remark-parse : 将 Markdown 文本解析成抽象语法树(AST),就像是「翻译官」把中文翻译成一种中间语言
- remark-math : 处理数学公式,让你的文档可以「高大上」地展示复杂数学表达式
- remark-rehype : 将 Markdown AST 转换成 HTML AST,相当于「转换器」把中间语言翻译成另一种中间语言
- remark-gfm : 支持 GitHub 风格的 Markdown 扩展功能,比如表格、任务列表等,让你的 Markdown 「与时俱进」
- remark-flexible-containers : 提供灵活的容器功能,让你的内容布局更加多样化,就像是给内容「准备了各种形状的容器」
4. Rehype 家族 - HTML 的「美容师」
- rehype-raw : 保留原始 HTML,让你的 Markdown 中混合的 HTML 代码也能正常工作,相当于「允许特殊人才保留自己的特色」
- rehype-katex : 将数学公式渲染成漂亮的 HTML,让数学表达式「穿上漂亮的衣服」
- rehype-highlight : 为代码块提供语法高亮,让你的代码「光彩照人」
5. 样式支持 - 颜值担当
- katex.min.css : 数学公式的「时尚服饰」
- github-dark.css : 代码高亮的「炫酷皮肤」
实现原理大揭秘 - 从文本到页面的神奇旅程
1. 组件结构设计
组件使用 Vue3 的 defineComponent 定义,接收一个必须的 markstr 属性,这是要解析的 Markdown 字符串。整个组件的设计非常简洁,就像一个「专注的翻译官」,只做一件事,但要做到极致!
2. 解析器链的构建
let unifiedProcessor = computed(() => {
const processor = unified()
.use(remarkParse, { allowDangerousHtml: true})
.use(remarkFlexibleContainers)
.use(remarkRehype, { allowDangerousHtml: true})
.use(rehypeRaw)
.use(remarkGfm)
.use(rehypeKatex)
.use(remarkMath)
.use(rehypeHighlight);
return processor;
});
这部分代码构建了一个「解析流水线」,就像工厂里的生产线一样,Markdown 文本会依次经过各个「加工环节」。这里使用 computed 确保解析器只在必要时重新创建,提高了性能。
3. 文件转换与 AST 处理
const createFile = (markstr) => {
const file = new VFile();
file.value = markstr;
return file;
};
const generateVueNode = (tree) => {
const vueVnode = toJsxRuntime(tree, {
Fragment,
jsx: jsx,
jsxs: jsxs,
passNode: true,
});
return vueVnode;
};
这两个函数分别负责:
- createFile : 将 Markdown 字符串包装成 VFile 对象,就像是给文本「准备好行李,准备出发」
- generateVueNode : 将解析后的 AST 树转换成 Vue 的虚拟 DOM 节点,相当于「将中间语言翻译成最终的目标语言」
4. 响应式渲染
const computedVNode = computed(() => {
const processor = unifiedProcessor.value;
const file = createFile(props.markstr);
let result = generateVueNode(processor.runSync(processor.parse(file), file));
return result;
});
return () => {
return h(computedVNode.value);
};
这里是整个组件的「核心驱动」:
- 使用 computed 响应式地计算虚拟 DOM,当 markstr 变化时,会自动重新解析并渲染
- processor.parse(file) 将文件解析成 AST
- processor.runSync(...) 运行所有插件处理 AST
- 最后通过 h() 函数将生成的虚拟 DOM 渲染到页面上
技术亮点与设计精髓
- 响应式设计 : 利用 Vue3 的 computed ,实现了 Markdown 字符串变化时的自动重新解析和渲染
- 模块化插件链 : 采用统一的插件系统,各功能模块解耦,可以灵活地添加或移除功能
- 高性能优化 : 通过 computed 缓存解析器和虚拟 DOM,避免不必要的重复计算
- 丰富的功能支持 : 支持数学公式、代码高亮、GitHub 风格扩展等高级功能
- 错误处理机制 : 提供了 errorCaptured 钩子,捕获并记录解析过程中的错误
代码优化建议
虽然这个组件已经相当优秀,但还有一些小地方可以进一步完善:
- 插件顺序优化 : 目前的插件顺序可能不是最优的,建议调整为更合理的顺序:
const processor = unified()
.use(remarkParse, { allowDangerousHtml: true})
.use(remarkGfm) // GFM 应该在 early 阶段
.use(remarkMath) // 数学支持也应该 early
.use(remarkFlexibleContainers)
.use(remarkRehype, { allowDangerousHtml: true})
.use(rehypeRaw)
.use(rehypeHighlight) // 代码高亮应该在 katex 之前
.use(rehypeKatex); // 数学渲染作为最后一步
- 异步解析支持 : 考虑添加异步解析模式,对于大型文档可以提高性能和用户体验
- 缓存机制 : 可以添加基于内容哈希的缓存,避免相同内容的重复解析
- 错误边界 : 增强错误处理,提供更友好的错误提示给用户
总结
这个 Vue3 Markdown 解析组件就像是一个「智能翻译官 + 高级排版师」,它不仅能准确地将 Markdown 转换成 HTML,还能让最终的展示效果既美观又功能丰富。通过巧妙地组合各种开源工具,它实现了一个功能完备、性能优良的 Markdown 解析渲染系统。
无论是构建博客、文档系统还是知识库,这个组件都能为你的项目增添强大的内容展示能力。希望这篇文章能帮助你理解这个组件的实现原理,也欢迎大家提出宝贵的改进建议!
最后,如果你觉得这个组件对你有帮助,不妨点个赞并分享给更多的开发者朋友,让我们一起让 Markdown 解析变得更简单、更强大!
GitHub源码仓库地址 如果觉得好用,欢迎给个Star ⭐️ 支持一下!