阅读视图

发现新文章,点击刷新页面。

如何自己构建一个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 渲染到页面上

技术亮点与设计精髓

  1. 响应式设计 : 利用 Vue3 的 computed ,实现了 Markdown 字符串变化时的自动重新解析和渲染
  2. 模块化插件链 : 采用统一的插件系统,各功能模块解耦,可以灵活地添加或移除功能
  3. 高性能优化 : 通过 computed 缓存解析器和虚拟 DOM,避免不必要的重复计算
  4. 丰富的功能支持 : 支持数学公式、代码高亮、GitHub 风格扩展等高级功能
  5. 错误处理机制 : 提供了 errorCaptured 钩子,捕获并记录解析过程中的错误

代码优化建议

虽然这个组件已经相当优秀,但还有一些小地方可以进一步完善:

  1. 插件顺序优化 : 目前的插件顺序可能不是最优的,建议调整为更合理的顺序:
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); // 数学渲染作为最后一步
  1. 异步解析支持 : 考虑添加异步解析模式,对于大型文档可以提高性能和用户体验
  2. 缓存机制 : 可以添加基于内容哈希的缓存,避免相同内容的重复解析
  3. 错误边界 : 增强错误处理,提供更友好的错误提示给用户

总结

这个 Vue3 Markdown 解析组件就像是一个「智能翻译官 + 高级排版师」,它不仅能准确地将 Markdown 转换成 HTML,还能让最终的展示效果既美观又功能丰富。通过巧妙地组合各种开源工具,它实现了一个功能完备、性能优良的 Markdown 解析渲染系统。

无论是构建博客、文档系统还是知识库,这个组件都能为你的项目增添强大的内容展示能力。希望这篇文章能帮助你理解这个组件的实现原理,也欢迎大家提出宝贵的改进建议!

最后,如果你觉得这个组件对你有帮助,不妨点个赞并分享给更多的开发者朋友,让我们一起让 Markdown 解析变得更简单、更强大!

GitHub源码仓库地址 如果觉得好用,欢迎给个Star ⭐️ 支持一下!

❌