1. 为什么 Markdown 画图首选 Mermaid:四条技术路线的对比
1.1 嵌入式 DSL 语法,Markdown 图表的最佳形态
我在项目里维护文档已经好几年了,一个非常深的体会是:技术文档最耗时间的其实不是文字,而是图。过去画流程图用的是 draw.io 或者 ProcessOn,画完截图贴进文档,图是好看,但一旦逻辑改一点,就得重新打开工具、重新截图、重新上传,Git 里完全看不出图的变化。后来换到 Mermaid 之后,这个问题才算真正解决。
Mermaid 的核心思路,是让你用一种接近自然语言的描述性语法(DSL)去定义图,而不是用手去拖动节点。它跟 Markdown 天然是绝配:Markdown 管文字,Mermaid 管图,两者都保存在同一个文本文件里,都能被版本管理系统追踪,都能在编辑器中直接预览。你改动一行语法,下次重新渲染时图就变了,整个过程没有“图片”这个概念。
这一点在多人协作时尤其有价值。代码评审时,同事可以直接在评论里指出“第 38 行这个箭头方向错了”,而不是对着截图说“左上角那个方形后面”。文档里所有图的状态、版本、改动记录都跟着代码走,任何一次修改都有迹可循,对团队的知识沉淀来说,这是截图方案完全给不了的体验。
1.2 四条画图路线的实际对比,为什么 Mermaid 更适合日常使用
先把我这些年用过的几种方式拉出来对比一下,方便你判断什么场景该用什么。
- 路线 A:Mermaid 等嵌入式 DSL 语法。最典型的代表是 Mermaid,偶尔也能看到使用 PlantUML 的项目。特点是纯文本描述、自动布局、Git 友好。缺点是复杂图表一旦规模变大,语法行数会拉得很长,而且自动布局意味着你无法像绘图软件那样精确控制每个节点的坐标位置。
- 路线 B:PlantUML。功能上跟 Mermaid 重叠度很高,时序图、用例图、类图都能画。它的语法更“工程化”,支持的种类也更全,但引入成本稍高:本地运行需要 Java 环境,虽然也可以用远程服务器渲染,但个人使用体验不如 Mermaid 轻量。如果你只是要在 Markdown 里画几张常见的图,Mermaid 的语法明显更友好。
- 路线 C:ASCII 字符画。用字符拼出框线和箭头,适合在纯文本终端或非常老旧的环境里展示。缺点也很致命:结构一调整,整张图就得重新手排,加一行文字可能要让后面所有字符全部右移,日常维护成本远高于收益,只适合临时“画个示意”。
- 路线 D:外部绘图工具导出图片。Visio、draw.io、ProcessOn、XMind 都算这一类。优点是样式精美、布局自由,适合做汇报材料;缺点是图片跟文档分离,改一次图就要重新导一次,而且图片内容无法被检索、无法 diff,时间一长,文档目录里全是“流程图最终版3.png”这种文件,根本分不清哪张对应哪版内容。
| 对比维度 | Mermaid | PlantUML | ASCII 画 | 外部工具截图 |
|---|---|---|---|---|
| 语法学习成本 | 低 | 中 | 低 | 无 |
| 与 Markdown 集成 | 原生配合 | 需插件配合 | 直接嵌入 | 图片嵌入 |
| Git 可追踪性 | 好 | 好 | 好 | 差 |
| 自动布局 | 有 | 有 | 无 | 手动布局 |
| 复杂图表支持 | 中 | 中高 | 差 | 高 |
| 适合场景 | 技术文档、笔记、轻量排期 | 大型工程建模 | 终端环境 | 汇报材料、精确设计图 |
从这个表能看出,Mermaid 最大的价值是“性价比”:绝大多数 Markdown 使用场景都是画流程、画交互时序、画简单排期,Mermaid 正好覆盖了这几个高频需求,同时帮你省掉了图片管理这一整套麻烦事。它不是万能的,但在“写文档”这个上下文里,它是权衡之后最顺手的选择。
1.3 主流 Markdown 环境的兼容现状
选一个语法,最怕的是写完了没地方渲染。这里列一下我实测过的主流环境:
| 环境 | 支持方式 | 备注 |
|---|---|---|
| GitHub / GitLab | 原生支持 | 在 Markdown 代码块里标注 mermaid 即可 |
| 语雀 / 飞书文档 | 原生支持 | 部分高级语法存在版本限制 |
| Obsidian | 自带支持 | 预览模式直接渲染 |
| Typora | 自带支持 | 本地渲染体验不错 |
| VS Code | 插件支持 | 推荐 Markdown Preview Mermaid Support |
| VitePress / VuePress | 插件/内置 | 需要确认对应版本 |
如果你的团队主要在 GitHub 仓库里写文档,那么几乎零配置就可以直接用 Mermaid 画图。如果你用的是公司内部 Wiki 或在线文档平台,先花两分钟在编辑器里试一段最简单的渲染代码,确认平台支持的 Mermaid 版本,再决定要不要大规模铺开。不然辛辛苦苦写了一大堆图,上线一看渲染不出来,返工成本非常高。
2. 流程图代码实例:从方框箭头到复杂分支
2.1 最小可用示例:方向声明与基础节点
流程图是 Mermaid 里最常用的图类型,语法结构非常直观。最简单的四要素流程:
graph LR A[开始] --> B{是否登录?} B -->|是| C[进入首页] B -->|否| D[跳转登录页] C --> E[结束] D --> E第二行的graph LR表示整张图的方向是 Left to Right,也就是从左往右排。常见的还有TB(从上到下)、BT(从下到上)、RL(从右往左)。我个人给技术文档画流程时首选TB或LR,因为大部分流程图是“入口到出口”的线性逻辑,横着排或竖着排都容易读;当分支特别多的时候,LR能在宽屏上放下更多节点,阅读体验比竖着挤在一起好很多。
A[开始]表示创建一个叫“开始”的节点,方括号代表矩形。B{是否登录?}用花括号表示判断节点,也就是菱形,这是流程图中“条件分支”的标准画法。-->是实线箭头,-->|是|表示在箭头上标注文字。这套语法几乎不存在理解门槛,代码写得顺不顺,只取决于你是否能把业务分支拆清楚。
2.2 节点形状速查表:不同形状的语义
做流程图时经常遇到的问题是“这个环节该用什么形状”,网上搜“流程图各种框的含义”会得到很多说明。这里结合 Mermaid 的语法整理一个对应关系:
| 形状 | Mermaid 语法 | 语义 |
|---|---|---|
| 矩形 | A[文本] | 处理步骤、操作 |
| 圆角矩形 | A(文本) | 起止节点,常用于“开始/结束” |
| 菱形 | A{文本} | 判断、条件分支 |
| 圆形 | A((文本)) | 连接点、入口出口标识 |
| 平行四边形 | A[/文本/] | 输入输出 |
| 六边形 | A{{文本}} | 准备步骤、异常处理 |
这套形状语义跟通用流程图的画法基本一致。实际使用中不用太纠结某个形状是否“绝对标准”,Mermaid 里影响可读性的关键其实是连线的方向是否统一、分支是否清晰,而不是节点形状的细微差异。只需要记住几个高频形状:矩形代表操作、菱形代表判断、圆角矩形代表起止,就足够覆盖大部分场景。
2.3 连线类型:实线、虚线、粗线、带标签线
连线是流程图里表达逻辑关系的关键。Mermaid 提供了几种常用连线形式:
| 效果 | 语法 |
|---|---|
| 实线箭头 | A --> B |
| 实线无箭头 | A --- B |
| 虚线箭头 | A -.-> B |
| 粗实线箭头 | A ==> B |
| 带文字箭头 | A -- 请求 --> B或 `A --> |
| 带文字虚线 | A -. 回调 .-> B |
举一个实际注册模块的例子,把几种连线混用起来:
graph TB U[用户提交注册表单] -->|POST /register| S[服务端校验] S -->|参数不合法| F[返回 400] S -->|校验通过| D[(写入用户表)] D -.->|触发异步事件| Q[发送欢迎邮件] D --> R[返回 201] F --> R Q -.-> R在这个例子里,[(用户表)]用了数据库节点的形状,-.->表示异步触发的动作。这种“主流程实线、旁路虚线”的习惯能帮读者立刻分清核心链路和辅助链路,算是我在实际项目中用过多次的小技巧。推荐每个项目里约定好:同步主流程统一用实线,异步、通知、回调这类旁路统一用虚线,整篇文档读起来会有很强的一致性。
2.4 子图分组:把复杂的业务模块拆开
当流程涉及多个子系统或模块时,建议用subgraph做分组。下面是一个用户管理模块的示例,逻辑不复杂,但分组后结构一目了然:
graph TB subgraph 前端 A[用户列表页] B[新增用户弹窗] end subgraph 后端 C[用户管理接口] D[权限校验服务] end subgraph 数据库 E[(用户表)] F[(角色表)] end A --> B B -->|提交| C C --> D D -->|通过| E D -->|通过| F C -->|写入成功| A使用 subgraph 时有三个容易踩的坑。第一,子图里的节点 id 必须全局唯一,哪怕两个子图之间毫无关系,也不能共用同一个 id,否则渲染会错乱。第二,子图的end必须与subgraph成对出现,少写一个直接渲染失败。第三,不要指望通过调整代码内的前后顺序来控制子图在画布上的具体位置,Mermaid 的自动布局引擎会根据连线关系重新排布,刻意“摆位置”是没用的。
顺带说一个容易混淆的概念:BPMN 里的网关(Gateway)是一种可执行的流程建模标准,它有自己的一套菱形、叉号符号,用于 Flowable、Camunda 这类工作流引擎驱动实际流程实例。Mermaid 画的流程图更像是“给人看的逻辑图”,不是可以直接跑起来的流程模型。如果你的目标是让流程引擎自动执行,应该用专门的 BPMN 建模工具导出 bpmn 文件,而不是在 Mermaid 里画一个“看起来差不多”的图。
3. 时序图代码实例:把一次 API 调用的完整过程画清楚
3.1 参与者、消息与激活框
时序图适合表达“多个角色之间按时间顺序发生的交互”,在接口设计、排查线上问题时特别好用。我画接口调用链时最常用的模板是:
sequenceDiagram participant User as 用户 participant Client as 客户端 participant Server as 服务端 User->>Client: 打开应用 Client->>Server: POST /api/login activate Server Server-->>Client: 返回 token deactivate Server Client-->>User: 展示登录成功participant User as 用户在这里给参与者起了中文别名。渲染时图上只显示“用户”,代码里用User来引用。这个特性在参与者英文名很长或来自不同系统时非常好用:代码还是保持英文标识符,展示出来却是中文,团队成员不用费力去对应“TS-API-Gateway-01”到底是谁。->>是实线箭头,表示同步请求;-->>是虚线箭头,表示返回结果。这种一实一虚的搭配能清楚刻画“请求-响应”模型。
activate Server和deactivate Server会为服务端画一个竖向的激活框(生命线),表示它在处理请求期间一直处于活动状态。排查超时和慢接口问题时,激活框能直观显示哪个服务长时间占用,可读性提升很多。简单图没有太多交互角色时可以不加,但角色一多,建议每个处理节点都标上,否则很难看清谁在什么时间段内处理请求。
3.2 分支、循环与并行:表达真实交互逻辑
真实系统的时序往往不是一根直线,而是包含成功失败分支、重试、并发调用等结构。Mermaid 的时序图支持alt、loop、par、opt、critical这些逻辑块,语法跟伪代码很接近:
sequenceDiagram participant App participant Gateway participant Auth participant Redis participant DB App->>Gateway: 请求业务接口 Gateway->>Auth: 校验 token alt token 有效 Auth-->>Gateway: 用户信息 Gateway->>Redis: 查询缓存 alt 命中缓存 Redis-->>Gateway: 缓存数据 else 未命中缓存 Gateway->>DB: 查询数据库 DB-->>Gateway: 数据 end else token 无效 Auth-->>Gateway: 401 Gateway-->>App: 拒绝访问 end Gateway-->>App: 业务响应这是一段典型的 Spring Boot 后端处理请求时会遇到的路径:网关校验 token,查缓存,命中则直接返回,未命中则查数据库。把这段画出来,比在文档里用大段文字描述分支路径清楚太多。alt/else/end就是“如果-否则”的分支,loop是循环,par是并行,opt是可选项。嵌套时强烈建议用缩进对齐,我见过很多语法错误,最后排查下来都是缩进乱了、end配对找不到导致的。
3.3 硬件信号时序图:为什么我不推荐用 Mermaid
搜索“i2c时序图”“spi正常通信时序图”“smbus通讯协议各种时序图”这类内容的人,需要先明确一点:Mermaid 的时序图本质是“消息序列图”,表达的是对象之间的调用顺序;而 I2C/SPI/SMBus 这类硬件时序图,需要画的是各信号线在时间轴上的高低电平变化,时钟周期、建立时间、保持时间都是精确的指标。用 Mermaid 去画这些内容会非常别扭,渲染出来也很难看。
硬件时序图我一般用 Wavedrom,它也是纯文本描述方案,通过一段 JSON 渲染成标准的数字波形图,支持时钟、注释、总线信号。一个最小示例:
{ "signal": [ { "name": "clk", "wave": "p.....|..." }, { "name": "sda", "wave": "x.3.=.x|...", "data": ["A0","A1"] }, { "name": "scl", "wave": "0.1.0.1.|..." } ]}输出就是带刻度的数字波形图,适合放在硬件设计文档里。结论其实很简单:软件交互时序用 Mermaid,硬件信号波形用 Wavedrom,各司其职,不要因为 Mermaid 方便就强行用来画所有类型的图。选工具之前先想清楚图表的本质用途,能省掉后面一大半返工时间。
4. 甘特图代码实例:把项目排期写进 Markdown
4.1 基础结构:section、任务状态与里程碑
甘特图是项目管理中很常见的视图,Mermaid 对它的支持能把排期直接放进 Markdown,跟代码、文档一起维护。一个基础示例:
gantt title 网站改版项目计划 dateFormat YYYY-MM-DD section 需求阶段 用户调研 :done, t1, 2025-03-01, 7d 原型设计 :done, t2, after t1, 5d section 开发阶段 前端页面 :active, t3, after t2, 10d 后端接口 : t4, after t2, 8d 联调测试 : t5, after t3, 5d section 上线阶段 预发验证 :t6, after t5, 3d 正式发布 :milestone, m1, after t6, 0ddateFormat YYYY-MM-DD指定日期格式;section用来分泳道;任务行里冒号后面依次是状态(done、active)和任务 id,以及时间定义。after t1表示该任务从t1任务结束后开始,这是表达依赖关系最方便的方式。里程碑用milestone标识,时长写0d即可,渲染出来是一个独立的菱形标记。
4.2 关键路径、时间格式与依赖规则
甘特图里有个容易被忽略但很有用的标记:crit,表示关键路径。关键路径上的任务一旦延期,整个项目会跟着延期。用法是在任务行里加上crit:
gantt title 迭代排期 dateFormat YYYY-MM-DD section 开发 设计评审 :crit, d1, 2025-04-01, 2d 编码实现 :crit, d2, after d1, 6d 单元测试 :d3, after d2, 3d 集成测试 :crit, d4, after d3, 2d渲染出来后,关键路径上的任务条会以红色显示,一眼就能看出哪些环节不能拖。日期既可以用YYYY-MM-DD格式写绝对时间,也可以像上面这样全部用after id的相对写法。如果团队习惯按迭代排期,我推荐尽量用相对写法:改动时只需要动第一个任务的日期,后面的自动顺延,比手动改每一个绝对日期省事得多,也不容易改漏。
4.3 和 Excel 甘特图相比,它的优势到底在哪里
搜“甘特图excel制作教程”的人,通常是要画一个可以用来做汇报的漂亮图表。Excel 甘特图本质上是用单元格颜色填充出的条形图,做出来确实美观,手动调整也方便,适合一次性汇报场合。但它的维护成本很现实:单元格要一格一格涂色,日期列要手动对齐,更新排期后图形和数据容易错位,一旦项目条目多起来,整个表格操作起来非常痛苦。
Mermaid 甘特图则适合在代码仓库里维护,它跟着代码评审走同一套流程。排期发生变化,Git 的 diff 能精确显示是哪一行改了;排期版本可以在历史记录里随时找回。它不适合做像素级漂亮的对外宣传图,但作为团队内部执行的排期表,效率和清晰度都足够。我自己通常的做法是:内部排期用 Mermaid 放在仓库里维护,对外汇报时把导出的图贴进 PPT,两头都不耽误。
5. 实操环节:编辑器配置与图表渲染不出来的排查思路
5.1 VS Code 预览 Mermaid 的方案
VS Code 是很多写 Markdown 的人的主力编辑器。默认情况下,VS Code 的 Markdown 预览并不渲染 Mermaid 图表,需要装插件。我常用的组合是安装Markdown Preview Mermaid Support插件,装完后按Ctrl+Shift+V(Mac 上Cmd+Shift+V)打开预览,代码块里的 Mermaid 语法就会变成图。这是目前我最推荐的本地编辑体验:一边写文档一边预览图,调样式不用来回切换浏览器。
这里有一个非常容易踩的点:代码块的语言标注必须写成mermaid,也就是代码块围栏写成```mermaid。很多人把 Mermaid 代码写在```text或者普通代码块里,编辑器自然不会去解析它。说明一下:本文中为了排版方便统一用```markdown展示代码,你实际写进 Markdown 文档时,围栏语言一定要换成mermaid,否则在 GitHub 和 VS Code 里都渲染不出来。
5.2 渲染失败时的系统排查链路
图表渲染不出来的时候,我的排查顺序基本是固定的,按这个顺序走能省很多时间:
- 打开 mermaid.live,把代码原样粘贴进去。如果在线预览也报错,就是语法本身的问题,直接看错误提示修。
- 如果在线环境正常,回到编辑器里检查代码块语言标注是不是
mermaid,语言写错是最常见的低级原因。 - 确认编辑器插件或平台使用的 Mermaid 版本。
quadrantChart、block这类比较新的图类型,在旧版本里不支持。 - 逐行精简代码,用“删一半看能否渲染”的二分法定位出错的那一行。高发点包括:中文括号混用、节点 id 包含空格、文本里出现未转义的
{}等特殊字符。 - 如果节点文本里确实需要包含花括号、管道符这类特殊字符,用引号包起来,比如
A["请求 {user} 数据"]。
这条链路里最关键的是第 1 步。mermaid.live 是官方在线编辑器,它给的错误提示通常很明确,把报错信息翻译成人话后,绝大多数语法问题都能在几分钟内解决。不要在不知道是不是语法错误的情况下盲目改代码,先定位再动手。
5.3 为什么 mermaid.live 手动编辑后,流程图样式会大变
这是一个被搜索了很多次的问题。很多人用 mermaid.live 画好一张图,手动拖动节点摆好了位置,然后发现只要加一个节点或者改一条连线,整张图的布局就完全乱掉,之前调整的位置全部失效。
这不是操作失误,而是 Mermaid 的设计如此。Mermaid 的渲染默认走 dagre 自动布局引擎(部分场景可切换 elk),它的工作方式是根据图的拓扑结构重新计算所有节点坐标。你手动调整的位置只停留在那一次渲染的结果里,下次结构一变,引擎会基于新结构重新计算一遍坐标,之前的手动微调自然就丢失了。所以正确的使用心态是:Mermaid 是用来快速生成“可读性尚可”的图的,不是用来做像素级精确设计的工具。
想让布局更可控,能做的有几件事:调整节点的定义顺序,让分支按阅读方向排列;用subgraph把关联节点分组,减少跨组连线;控制连线的方向和稠密度。如果确实需要精确摆布节点位置、调整每一根线的布局,建议导出到 draw.io 或 Figma 这类手动绘图工具里继续编辑,而不是在 mermaid.live 里死磕自动布局。
6. 进阶玩法:主题配置、AI 辅助生成与文档工作流整合
6.1 用主题配置统一团队图表风格
团队文档如果每人用不同配色,整体观感会很乱。Mermaid 支持在图表顶部声明初始化配置:
%%{init: {'theme': 'forest'}}%% graph LR A[需求] --> B[开发] B --> C[测试] C --> D[发布]内置主题包括default、neutral、dark、forest、base。深色模式文档可以用dark;大多数团队文档用forest或neutral比较耐看。也可以在配置里覆盖具体主题变量,比如调整主题色、字体大小、背景色,不过这部分通常只在给客户出文档时需要用到,日常使用用内置主题就够。建议在团队文档模板头部统一放一段 init 配置,至少能保证所有图风格一致。
6.2 用 AI 生成 Mermaid 草稿,再人工校正
现在很多 AI 编程工具已经内置了生成 Mermaid 图表的能力,比如你可能会搜到“claude code 生成时序图的 skill”这类场景,就是让 AI 根据代码或描述直接产出时序图。我实际用下来的感受是:AI 生成 Mermaid 是个很好的起点,比自己面对空白文档敲语法快得多,尤其是那些“我脑子里有逻辑,但不知道 Mermaid 措辞怎么写”的情况。
但 AI 生成的代码不能直接无脑用,常见的坑有三个:一是节点 id 重复,尤其是从多段文本里拼接逻辑时;二是loop、alt、par的嵌套层级和end数量对不上,渲染时直接报错;三是参与者别名定义和后续引用不一致。我的做法是:让 AI 生成后再丢进 mermaid.live 过一遍,有报错就把报错信息原样贴回去让 AI 修,一般一两轮就能稳定。提示词里记得带上“使用 Mermaid 语法”“参与者使用中文别名”“节点 id 保持唯一”这些具体要求,生成的图会更贴近你的需求。
6.3 导出与集成:从文档到发布会话
Mermaid 图表毕竟是在浏览器和编辑器里渲染的,如果哪一天需要把它放进 PPT 或者对外文档,可以用官方命令行工具导出图片。执行:
npx -p @mermaid-js/mermaid-cli mmdc -i input.mmd -o output.svg这条命令会读取一个.mmd文件,导出对应的 SVG 或 PNG。配合 CI 流程,还能在每次文档改动时自动重新导出图表,确保对外发布的图片和仓库里的代码同步,不会出现“文档改了图没改”的老问题。如果你用的是 VitePress、VuePress 这类静态站点工具,它们基本都支持 Mermaid 集成或官方插件,可以把文章里的图表直接渲染到站点上,跟文档整体风格保持一致。
我在实际项目中的体会是,把图变成文本的收益是长久的。用 Mermaid 之后最明显的变化是,评审会上讨论到某个分支逻辑时,我可以直接当场改文档里的那段 Mermaid 代码,刷新预览后大家接着讨论,整个过程像改代码一样自然。如果你刚开始接触 Mermaid,建议先只掌握流程图和时序图这两种,把语法基础打牢后再碰甘特图;另外一个小建议是,一个图一个代码块,别把整篇文档里所有图塞进同一个 Mermaid 块里,出问题时定位快很多,维护起来也清爽。从第一张流程图开始,你很快会发现,Markdown 里画图这件事,其实可以像写字一样自由。