去年我在团队里整理一份服务接入文档,系统里涉及登录、限流、回调、告警四条链路。用画图工具画了一下午,刚保存完产品过来说流程改了。我那个瞬间真的想把显示器关了。后来同事说:要不你试试Mermaid,把图写成代码,放进Markdown里,改的时候只改箭头就行。当天我把所有链路图重写成了Mermaid源码,从那以后,文档里任何一张图都能跟着代码一起Review了。
这篇东西我不想写成一份逐条罗列的官方语法手册,而是按我实际的使用路径来聊:先搞清楚Mermaid解决了什么问题,再上手画第一张流程图,接着扩展到时序图、状态图、甘特图这些常用图型,然后聊Live Editor、CLI和API这三件套怎么提高效率,最后重点讲一讲跨平台兼容——毕竟代码写得再漂亮,放到不支持的平台上渲染不出来也是白搭。适合正在写技术文档、项目README、内部Wiki,以及想在博客或代码仓库里放图的人参考。
1. 为什么把图画成文本:从“改图艰难”到“图随代码走”
1.1 传统画图工具的痛,都是在改动时爆发
接触Mermaid之前,我很长一段时间都在用Visio和draw.io画架构图。单张图刚画完的时候确实挺好看,但问题从来不出在“画出来”那一刻,而是出在“要改”那一刻。
传统绘图文件大多是二进制格式或私有XML格式,离开对应工具根本打不开。你想让不熟悉这个工具的人帮你改一条线,要先教他装软件、学操作。更难受的是,这类文件进了Git之后,你没法像看代码一样看一张图到底改了什么,只能打开两个版本对比,肉眼找差异。团队里一旦有多个人同时维护一张架构图,很快就分不清谁改过哪一版了,历史上一团乱麻。
还有一类常见场景:代码逻辑变了,图却没有跟着变。很多人不是不想更新文档,而是改图成本太高——先要找到源文件,再拖线、拽框、重新导出、替换图片。流程一多,图的维护者干脆放弃了,最后文档里挂着一张“仅供参考”的历史图。
1.2 Mermaid把图形变成代码之后,发生了哪些变化
Mermaid的核心思路特别朴素:用类似Markdown的文本语法描述图形,然后由解析器渲染成SVG。你不需要关心画布坐标,不需要手动对齐节点,只要写下节点和连线之间的关系,渲染引擎会帮你排布。
图变成文本之后,最直接的好处就是可以进Git做版本管理。每次改动都能看到diff,A节点指向B节点改成了指向C节点,清清楚楚。代码评审的时候,图和代码在同一次MR里出现,逻辑变更一目了然。文档不再是事后补的“成品”,而是跟代码一起演化。
Mermaid还有另一个隐藏优势:它天然适配如今的内容生态。GitHub仓库的README可以直接渲染,很多笔记软件和在线文档平台也支持Mermaid代码块。你在本地写好的图,复制粘贴到支持Mermaid的平台,不需要再传图片,渲染由平台自动完成。
1.3 Mermaid不是万能的,它的适用边界要心里有数
我也见过一些不太合适的用法。有人试图用Mermaid画一张包含上百个节点的完整网络拓扑,结果渲染出来连成一片,可读性反而比手绘图差。有人追求极其精细的视觉设计,要求每个节点阴影、圆角完全一致,这也超出了Mermaid的定位。
文本化绘图适合的是结构清晰、节点数量适中、需要频繁维护的场景,比如流程图、时序图、状态机、部署关系、数据库ER图。如果你的图主要价值在视觉冲击力,或者需要像素级控制布局,建议还是用专业设计工具或绘图软件,导出图片之后用Mermaid画局部补充。判断标准很简单:这张图未来会不会改?会改就值得用文本描述,不会改怎么画都行。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 从第一张flowchart开始:读懂Mermaid图表编译语法
2.1 五行代码看懂一个流程图
Mermaid图表编译语法没有想象中复杂,核心就两样:节点怎么声明、节点之间怎么连线。先看一张最常见的登录鉴权流程图,源码就几行:
text复制flowchart TD
A[收到请求] --> B{权限校验}
B -- 通过 --> C[调用业务服务]
B -- 不通过 --> D[返回 403]
C --> E[写入审计日志]
第一行的 flowchart TD 是声明,表示这张图画的是流程图,方向是从上到下(TD = Top Down)。如果想从左侧往右侧排,可以写 flowchart LR,L是Left,R是Right。
接着每一行定义节点和连线。A[收到请求] 中,A 是节点ID,方括号里的“收到请求”是显示文本。B{权限校验} 中的花括号表示这是一个判断节点,渲染出来是菱形。箭头 --> 表示实线带箭头连线,-- 通过 --> 表示在线上标注文字。
这里要特别提醒新手一点:节点ID和显示文本是两回事。A 只是内部标识,你完全可以把ID换成 request、check、success 这种有意义的英文单词,显示文本用中文完全没问题。早期我用中文当ID,图简单时没事,一旦有子图或复杂跳转,某些旧版本渲染器会出边界问题,后来统一改成英文ID再也没踩过线。
2.2 flowchart和graph到底有什么区别
如果你翻过老教程,会看到 graph TD、graph LR 这类写法。这是Mermaid早期的语法,一直保留到今天,很多旧项目里还在用。而新项目里官方推荐的是 flowchart 关键字。
两者的底层布局算法不一样。flowchart在路径计算和节点排布上做了优化,画分支较多的流程时,线条交叉更少,整体更均衡。graph则更像老式实现,简单图没问题,复杂图偶尔会出现连线绕路的情况。
实际写作建议:在新文档、新平台里一律用 flowchart。但如果你要维护的是老系统里已经写好的存量图,不要着急全局替换,因为某些内置渲染器版本过低的平台可能不认识 flowchart 的某些新语法。语法兼容层面的问题,后面跨平台章节再展开。
2.3 画流程图最容易踩的语法坑
我在帮同事Review Mermaid代码时,发现新手踩坑集中在三个方面。
第一,括号不成对。方括号、花括号、圆括号在Mermaid语法里都有语义,少一个后括号,整个图解析失败。尤其是判断条件里写了中文括号,渲染器会把全角符号当成普通文本,根本不会报错,就是一个判断节点显示了你原本不想显示的字符。第二,连线文字没有写好分隔。想表达“满足条件时走A分支”,正确写法是 B -- 满足 --> A 或 B -->|满足| A,两个版本都不要省略两边的空格。第三,把节点文本写得过长。一个节点里塞一大段话,渲染出来会变成又宽又扁的矩形,阅读体验很差。长文本应该拆成描述放节点里,细节写在文档正文里。
还有一个非常实用的小技巧:判断节点和终止节点要有明确的出口。我见过不少流程图,菱形判断只画了“是”的方向,“否”的方向没有落点,图看起来像是逻辑没走完,连代码Review的人都会误以为少写了分支。任何判断节点,所有可能的走向都要有一条明确的边。
3. 按场景选图型:时序图、状态图与甘特图快速上手
流程图是Mermaid里的第一课,但实际工作里我用的更多的其实是时序图、状态图和甘特图。如果一张流程图的核心是“做什么,怎么做”,那时序图的核心就是“谁和谁之间按什么顺序沟通”。
3.1 时序图:接口调用、登录认证场景首选
团队里前后端联调时,最常出现的争议就是调用顺序不明确。文字描述很长,又容易产生歧义,用一张时序图说清楚谁先调谁,效率高得多。
text复制sequenceDiagram
autonumber
participant U as 用户终端
participant S as 服务端
U->>S: 发起登录请求
S-->>U: 返回登录凭证
你不需要记忆太多语法,先掌握三部分:participant 声明参与者,->> 表示同步调用,-->> 表示返回。参与者默认渲染在顶部,引号后可以给参与者起中文别名。autonumber 表示自动编号,特别适合写接口调用链,别人一眼能看到第几次交互是哪一步。
实际使用中,有的团队会刻意区分实线和虚线。如果我调用你、等你返回,我用 ->>;如果我发消息给你但不等待结果,用 -)。返回路径一般用虚线 -->>。这样语义更干净,图和代码里的消息类型能对上。
3.2 状态图:状态机比流程图更适合表达生命周期
如果业务对象存在多个稳定状态,而且状态之间要约束迁移条件,流程图画起来会很别扭,这类场景应该用状态图。状态图的关键字是 stateDiagram-v2,我习惯把它理解成“给状态机画一张迁移表”。
text复制stateDiagram-v2
[*] --> 待支付
待支付 --> 已支付: 支付成功
待支付 --> 已取消: 超时未支付
已支付 --> 已退款: 申请退款通过
已退款 --> [*]
已取消 --> [*]
[*] 表示初始状态或结束状态,待支付 --> 已支付: 支付成功 表示一次状态迁移,冒号后是触发条件。这套语法表达订单生命周期、工单流转、服务进程状态都非常合适。
和普通流程图比,状态图强制你梳理状态本身,而不是只画流程分支。画完通常会发现之前漏了临界状态,比如“已取消”之后能不能“重新支付”?这些思考比图本身更有价值。
3.3 甘特图、类图与饼图:按需使用,别贪多
甘特图在部分团队里被用来做项目排期,Mermaid的甘特图语法比较直白,可以按部门或模块分section,支持开始时间和持续时长。如果你只在Wiki里简单排一下迭代计划,不依赖专业项目管理工具,它足够用。
text复制gantt
title 示例迭代排期
dateFormat YYYY-MM-DD
section 开发
需求评审 :done, a1, 2026-05-01, 2d
后端接口开发 :active, a2, 2026-05-03, 5d
前端页面联调 :a3, after a2, 3d
done 表示已完成,active 表示进行中,after a2 是一种任务依赖写法,表示这个任务在a2之后开始。类图和ER图在系统设计文档里也很常用,classDiagram 用于表现类之间的继承、组合关系,erDiagram 用于表现数据库表关系。不过这两类图用文本描述时,节点和关系一旦多起来,可读性会下降,画之前先确认是否有必要。
给新手的建议是不要一次性把Mermaid支持的十余种图型全学一遍。先掌握流程图、时序图、状态图,处理80%的技术文档场景就够了,碰到具体需求时再回头查甘特图、类图等语法。
4. 调图、出图、嵌图:Live Editor、CLI与API三件套
4.1 Mermaid Live Editor是最低成本的调试入口
不管你是Mermaid新手还是老手,在写复杂图前把代码粘到Mermaid Live Editor里调试,永远比直接在文档里试错快。Live Editor左边是源码区,右边是实时渲染画布,只要源码变化,图形会立刻重新编译。语法出错时,页面会给出错误提示,并且尽量定位到出错位置。
一个比较容易忽略的用法是右上角的导出功能。Live Editor可以导出PNG和SVG,如果你只是临时要把图插到某个不支持Mermaid的文档里,这个入口比搭CLI环境快得多。它还有个“复制Markdown”按钮,会把当前图生成一段Mermaid代码块,粘贴到GitHub或语雀这类支持Mermaid的平台就能直接渲染。
我的建议是,Live Editor适合交互式调试,CLI适合批量处理和自动化,API适合嵌入到自有页面。三者分工不同,侧重点也不一样。
4.2 CLI的环境准备和批量出图
某些发布平台不支持Mermaid源码,这时候需要提前渲染成图片再上传。如果只有一两张图,用Live Editor导出就行;但如果文档里有十几张图,每次手动导出不现实,应该用命令行批量处理。
Mermaid官方CLI对应的包名是 @mermaid-js/mermaid-cli,全局安装后使用 mmdc 命令:
bash复制npm install -g @mermaid-js/mermaid-cli
mmdc -i input.mmd -o output.svg
mmdc -i input.mmd -o output.png
mmdc -i input.mmd -o output.pdf
CLI实际是调起Puppeteer无头浏览器,把Mermaid源码渲染成图片或矢量图,所以安装时经常涉及Chromium下载。公司内网环境如果下载慢,可以通过Puppeteer的配置指向本地可用的浏览器路径,具体需要增加一份Puppeteer配置文件,并在文件中指定 executablePath。
批量渲染时,mmdc支持通配符:
bash复制mmdc -i "diagrams/*.mmd" -o "dist/"
注意输出目录要提前创建好,不然命令容易报错。还有两个参数非常常用:-b white 指定白色背景,-s 2 指定两倍分辨率,避免导出图片在Retina屏幕上发虚。
4.3 在自有页面里用Mermaid API
自己的站点或内部系统要集成Mermaid,有两种路线:一种是后端先把Mermaid渲染成图片再输出,另一种是前端直接调用Mermaid的JavaScript渲染器。第二种更灵活,因为用户看到的还是源码,交互上也更方便。
页面接入时,最小示例大概是:
html复制<script type="module">
import mermaid from "https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs";
mermaid.initialize({ startOnLoad: true });
</script>
<pre class="mermaid">
flowchart LR
A[前端页面] --> B[Mermaid 渲染器]
</pre>
mermaid.initialize 负责初始化配置,startOnLoad: true 表示页面加载后自动查找CSS类为 mermaid 的元素并渲染。如果你通过异步请求往页面里追加新的Mermaid源码,重新触发渲染时不要重复调用 initialize,应该调用 mermaid.run(),不然配置会被二次覆盖,甚至出现渲染失败。
5. 跨平台兼容的本质与处理策略:一份源码多端可用
5.1 为什么同一段Mermaid代码在不同平台渲染结果不一样
这是整个Mermaid使用链路里坑最多的一个环节,得先说清楚根源:Mermaid不是一个浏览器或Markdown原生标准,它是一套开源渲染器。不同平台对Mermaid的支持方式,是在各自的编辑器里内置了一个特定版本的Mermaid运行时。也就是说,语法能不能支持、支持到哪个程度,取决于平台内置的解析器版本。
GitHub有自己维护的渲染器,语雀有自己的实现,Typora内置的版本也可能和你的CLI版本完全不同。本地最新版Mermaid里能跑通的语法,放到某个两年前内置版本的平台,很可能直接解析报错。这不叫平台bug,本质是版本漂移。
想保证跨平台稳定,最稳妥的策略不是“用最新语法”,而是“用最朴素的语法”。在团队里有明确约定之前,我一般建议少用刚发布的新特性,多用老版本也兼容的语法,并且最后用目标平台实际验证一次。
5.2 主流内容平台支持情况一览
先把实际体验整理成一张表,方便按场景对照:
| 平台 | Mermaid支持情况 | 使用建议 |
|---|---|---|
| GitHub / GitLab | 原生支持Markdown代码块中渲染Mermaid | 可直接提交Mermaid源码 |
| 语雀 | 代码块支持Mermaid | 代码块选择mermaid语言即可 |
| 飞书文档 | 支持Mermaid | 代码块中选择Mermaid |
| Typora / Obsidian | 支持Mermaid | 本地写文档效率高 |
| VuePress / Vitepress | 默认不原生支持,需插件 | 用插件或自定义组件 |
| Notion | 不支持原生Mermaid | 需要先导出图片再上传 |
| 知乎/公众号后台 | 不支持Mermaid | 导出图片后插入 |
| 自建Web页面 | 可嵌入MermaidAPI | 自由度最高 |
这张表的结论很直接:原生支持Mermaid的平台,优先放源码;不支持Mermaid的平台,老老实实导出图片。有人问能不能通过平台私有的iframe或嵌入块曲线实现,答案是可以做,但不建议,因为维护成本和内容稳定性都不可控。
5.3 跨平台代码兼容的几条硬建议
第一,尽量避免在节点文本中使用HTML标签。Mermaid原本支持在节点内写<br/>、<b>这类HTML标签来做换行和加粗,但很多平台出于安全考虑会转义或过滤,结果你看到的是一段带尖括号的文本,图直接变脏。换行这类需求,能用拆节点解决的绝不依赖HTML标签。
第二,使用ASCII字符作为节点ID,保留文本用引号包裹或直接使用中文都可以。比如 A[用户输入] 比 用户输入[用户输入] 稳。某些旧渲染器对包含中文、空格和特殊符号的ID解析能力较差,一条边引用了错误ID,整张图编译失败。
第三,明确知道自己在用哪个版本的语法。写流程图优先 flowchart;但如果你的文档需要兼容很老的内容平台,验证时发现旧平台不支持,可以退回 graph 语法。状态图建议统一写 stateDiagram-v2,老版本可能只认旧 v1,这种情况只能在文档里标明最低版本要求。
第四,导出图片时注意字体。使用CLI导出PNG时,如果环境里的Chromium没有中文字体,导出的图会显示成方块。这个问题在Docker环境里特别常见,需要在镜像里安装中文字体,或者引用系统字体。比如基础镜像里没有Noto Sans CJK,就配上 fonts-noto-cjk 再导出。
5.4 安全策略不是附加项,而是前置条件
跨平台还有一个容易忽视的维度——安全性。Mermaid源码本身是文本,但渲染时会解析部分HTML和链接,尤其旧版本里存在点击节点链接跳转的能力。如果你把文档放在不可信环境中,或者你的平台允许用户粘贴Mermaid代码,一定要关注Mermaid配置里的安全级别。
Mermaid默认的 securityLevel 是 strict,在这个级别下,节点里的大部分HTML标签会被转义,不会变成可点击、可注入的DOM。除非你非常清楚自己在做什么,否则不要为了显示效果把安全级别改成 loose。这也是很多在线文档平台敢直接渲染用户Mermaid代码的原因。
6. 长期维护Mermaid图库:目录纪律、版本锁与查错路径
6.1 Mermaid源码要不要入库,放哪个目录
我的答案是:入库,而且建议单独建目录。不要把Mermaid源码只嵌在某篇文档的代码块里,如果你在一个大项目里维护很多图表,最好把所有 .mmd 源文件集中放到一个目录,例如 docs/diagrams/。然后在具体文档中通过相对路径引用或说明源文件位置。
为什么这样做?第一,源文件可被CLI批量处理,CI能统一校验和导出。第二,更重要的图可以纳入代码评审,而不是改完没人看。第三,防止同一个图在多个文档里复制出多份,改了一处漏了另一处。
如果目标平台只认图片,我的经验做法是:源文件在 docs/diagrams/ 里保留一份,再在文档里贴生成后的图片,并在图片下面一行写清源文件路径和修改方法,这样后续维护者不会拿到一张图片无从下手。
6.2 版本锁定是保证渲染一致性的唯一办法
如果你所在团队已经被“本地编译正常、线上渲染失败”搞过几次,你会明白版本锁定的重要性。具体做法有三个层面。
语法层面:在团队文档或项目根目录的README里标明“本仓库图表基于Mermaid 10.x验证”,遇到版本特性冲突时以老版本为准写语法。
工具层面:不要把Mermaid CLI全局安装在不同人机器上,局部安装并通过 package.json 固定版本。CI环境里最好使用锁文件,保证产物一致。
产物层面:如果同一份文档还要发布到不支持原生Mermaid的渠道,建议在CI里把源文件同时生成 svg/png 产物,并将图片进入构建流程。源文件和图片同时保留,文字文档使用图片,源码用于后续修改。线上用户看到的是图片,但维护者拿到的是源文件,两边都不亏。
6.3 排查Mermaid语法错误的一个稳定思路
遇到Mermaid解析不了的情况,先别急着怀疑渲染器。我把排查路径整理成一个固定顺序:先在Live Editor粘贴原图,本地复现问题;再逐步删除分支,不断缩小出错范围;等确定了出错行之后,对照括号、引号、连线的写法规范;最后拿到目标平台去渲染验证。
在Live Editor里已经正常的图,到了GitHub突然报错,通常是语法版本问题。这时候最有效的手段不是猜语法,而是查目标平台当前使用的Mermaid版本,以及针对这个版本的手册,用该版本支持的语法重写。相反,在GitHub上正常渲染的数字,放在Typora里出问题,也要优先考虑Typora内置版本的兼容情况,这是很多新手容易忽略的思路。
6.4 团队协作时的几条小约定
最后分享几条带团队后总结的约定,都是在实际协作中踩过坑后沉淀下来的。
一个仓库里只使用一个Mermaid语法风格,比如统一用 flowchart、stateDiagram-v2,避免一会儿用Lint一会儿用Rint;当非要用LR/TD等方向时,写清楚原因。节点命名保持英文且有意义,不要用 a1、b2 这种编号型ID。完成Mermaid图后,给每张图配一句话说明,防止别人看不清楚分支意图。
另外,如果让团队成员在PR里同时改了代码和Mermaid图,请在描述区说明两个主要改动点。代码Review的人需要核对图是否与代码逻辑一致。这个看似Mermaid之外的习惯,往往比语法技巧更重要。
我个人的体会是,Mermaid真正改变的不是画图效率,而是把“图”这件原本游离在文档体系外的东西,拉回到了版本管理和协作流程里。只要一开始把源文件放对地方、语法用得克制、目标平台的兼容验证做好,后面所有的更新都会变得非常顺畅。如果你还没试过,从下一张流程图开始,建议直接写Mermaid源码。
