在 VitePress 中集成 Mermaid 图表支持的完整指南

最近在写一个 VitePress 站点,想在文档中插入一些流程图和时序图,于是研究了一下如何在 VitePress 中集成 Mermaid。

参考来源:whlit’s blog | VitePress 支持 Mermaid

为 VitePress 添加 Mermaid 图表支持:从原理到实现

在现代技术文档中,图表的重要性不言而喻。它们能够清晰地展示流程、架构和关系,让复杂的概念变得直观易懂。Mermaid 作为一个基于文本的图表绘制工具,以其简洁的语法和强大的功能受到了开发者的广泛喜爱。

本文将详细介绍如何为 VitePress 文档添加 Mermaid 图表支持,从核心思路到具体实现,帮助你在自己的项目中集成这一实用功能。

为什么需要特殊配置?

VitePress 默认使用 markdown-it 作为 Markdown 解析器,虽然它功能强大,但并不直接支持 Mermaid 图表的渲染。这是因为 Mermaid 图表的渲染需要在浏览器端执行 JavaScript,而传统的 Markdown 解析是在服务端完成的。

因此,我们需要一种机制,能够在服务端构建时识别 Mermaid 代码块,并将其转换为可在客户端渲染的 Vue 组件。

实现方案概述

我们的解决方案分为四个核心步骤:

  1. 创建 markdown-it 插件识别 Mermaid 代码块
  2. 开发 Vue 组件负责客户端渲染
  3. 配置 VitePress 以集成插件和组件
  4. 解决构建过程中的模块加载问题

下面让我们详细看看每个步骤的具体实现。

定义 markdown-it 插件

我们首先创建了一个自定义的 markdown-it 插件 (mermaidPlugin.ts),可以在 plugins 文件夹下,用于拦截所有语言标记为 mermaid 的代码块:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// mermaidPlugin.ts
import type MarkdownIt from 'markdown-it';

export default function mermaidPlugin(md: MarkdownIt): void {
const fence = md.renderer.rules.fence?.bind(md.renderer.rules);

md.renderer.rules.fence = (tokens, idx, options, env, self) => {
const token = tokens[idx];
const language = token.info.trim();

if (language.startsWith('mermaid')) {
return `<Mermaid id="mermaid-${idx}" code="${encodeURIComponent(token.content)}"></Mermaid>`;
}

return fence!(tokens, idx, options, env, self);
};
}

这个插件的核心作用是拦截 Mermaid 代码块,将其替换为自定义的 Vue 组件标签,并将图表源代码作为属性传递给该组件。

定义 Mermaid Vue 组件

接下来,我们创建了 Mermaid.vue 组件,负责在客户端实际渲染图表:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<template>
<div class="mermaid-container" v-html="svgRef"></div>
</template>

<script setup lang="ts">
import { ref, onMounted } from 'vue';
import mermaid from 'mermaid';

const props = defineProps<{
id: string;
code: string;
}>();

const svgRef = ref('');

const renderMermaid = async (id: string, code: string) => {
mermaid.initialize({ startOnLoad: false });
const { svg } = await mermaid.render(id, code);
return svg;
};

onMounted(async () => {
svgRef.value = await renderMermaid(props.id, decodeURIComponent(props.code));
});
</script>

这个组件接收编码后的 Mermaid 代码,在组件挂载后调用 Mermaid API 进行渲染,并通过 v-html 指令显示生成的 SVG。

第三步:配置 VitePress

安装依赖

首先安装必要的依赖:

1
pnpm add -D mermaid @types/markdown-it

注册全局组件

在主题配置文件 (index.ts) 中全局注册 Mermaid 组件:

1
2
3
4
5
6
7
8
9
10
11
12
// .vitepress/theme/index.ts
import type { Theme } from 'vitepress';
import DefaultTheme from 'vitepress/theme';
import Mermaid from './components/Mermaid.vue';

export default {
extends: DefaultTheme,
enhanceApp: async ({ app }) => {
app.component('Mermaid', Mermaid);
},
} satisfies Theme;

配置 markdown-it 插件

在主配置文件 (config.mts) 中启用我们创建的插件:

1
2
3
4
5
6
7
8
9
10
// .vitepress/config.mts
import mermaidPlugin from './plugins/mermaidPlugin';

export default defineConfig({
markdown: {
config: (md) => {
md.use(mermaidPlugin);
},
},
});

第四步:解决构建问题

在配置过程中,我们遇到了一个常见的 ESM 模块加载错误。这是因为 VitePress 需要明确知道配置文件的模块格式。通过将配置文件从 config.ts 重命名为 config.mts,我们明确告知 VitePress 将其作为 ES 模块处理,从而解决了此问题。

使用示例

配置完成后,你可以在 Markdown 文件中直接使用 Mermaid 代码块:

1
2
3
4
5
6
7
```mermaid
graph TD
A[开始] --> B{判断}
B -->|是| C[执行操作]
B -->|否| D[结束]
C --> D
```
graph TD
    A[开始] --> B{判断}
    B -->|是| C[执行操作]
    B -->|否| D[结束]
    C --> D

VitePress 会自动将其渲染为对应的流程图。

总结

通过本文介绍的方法,我们成功为 VitePress 添加了 Mermaid 图表支持。这种方案的核心思路是:

  1. 在服务端构建时,通过 markdown-it 插件识别 Mermaid 代码块
  2. 将其替换为自定义 Vue 组件并传递代码参数
  3. 在客户端由 Vue 组件调用 Mermaid 库完成实际渲染

这种架构既保持了服务端构建的效率,又利用了客户端渲染的灵活性,是一种优雅的解决方案。