用Mermaid把文档图表变成可维护的文本源码:原理、工作流与避坑指南
记不清是第几次了为了改一张流程图里的一个判断分支我打开绘图软件重新拖了一遍箭头。图改完还要重新导出、重新上传然后打开聊天记录问群里的人拿的是不是最新版本。后来我把图表换成了 Mermaid 这类文本绘图方式这类问题才算彻底根治。简单说Mermaid 就是让图表变成像代码一样可以被修改、审查和追溯的文本源码配合 Markdown 文档一起使用写完即渲染改完即生效。这篇文章不打算照搬官方语法手册我想从一个长期使用者角度聊聊为什么它适合进入你的文档工作流、哪些图用起来最顺手以及那些官方文档不会告诉你的坑。1. 为什么说画图本质上是一次文本编辑1.1 团队文档协作里的真实痛点先说一个很常见的状况技术方案文档里放了一张架构图评审会上有人提意见说某个服务调用关系画反了。在传统绘图软件里你得先找到原始文件打开画布找到那条线重新调整方向或位置再导出新的图片。整个过程少说几分钟多则十几分钟。如果有三个人在不同时间各改了一版最后图片版本直接失控。Mermaid 解决的是相反的问题你不需要打开一个画布只需要在 Markdown 里写下图的文本描述。例如下图结构在源码里是这样的graph TD A[开始] -- B{是否满足条件} B -- 是 -- C[执行操作] B -- 否 -- D[结束]渲染出来是一张流程图如果觉得某个节点文本写得不对直接在文本里改保存后图片跟着变。最关键的差别在于所有改动都发生在文本里所以它能被版本管理系统跟踪。我能在历史记录里清楚地看到谁在什么时间把判断条件从是改成了否这在纯图片工作流里几乎做不到。1.2 文本绘图背后的本质逻辑从原理上看Mermaid 做的事情非常像写 HTML 而不是用画图工具画网页。它定义了一套极小的领域语言包含节点、连线、方向、子图、注释等有限的关键字渲染引擎拿到文本后先做词法分析、语法分析再交给布局引擎自动计算坐标和连线路径最终输出图形。之所以这条路能走通是因为很多技术场景下的图表本质上没有我们想象的那么自由。一个业务流程、一段接口调用、一份项目排期这些图的结构基础是节点和关系而节点和关系恰好能被文本清晰描述。当空间信息被降维成线性文本后会自动获得三个额外优势它可被 diff比对差异、可被复用复制模板、可被程序批量生成。对于写技术文档的人来说这三点比图片更漂亮重要得多。1.3 什么场景我不建议用 Mermaid话虽这么说我也不会把所有图表都塞给 Mermaid。遇到下面几类情况我更倾向使用专用绘图工具强调视觉表现力的图比如对外宣传海报、高保真界面流程图节点超过三四十个、层级混乱且依赖手工调整布局的复杂架构拓扑需要多人实时在线拖拽协作的白板式讨论场景。我自己的判断标准比较简单如果这张图的核心价值是表达清楚关系那 Mermaid 完全胜任如果核心价值是展示视觉美感或者允许自由布局那不如用现成的绘图软件。很多时候不是工具不够好而是把工具用错了地方。2. Mermaid 语法骨架四类高频图的个性与共性2.1 流程图从 graph 到 flowchart 的进化流程图是 Mermaid 里使用频率最高的图类型也是新手最先接触的。语法层面有两个常见关键字早期的graph和后来的flowchart。区别主要在于flowchart支持更多高级布局能力和更丰富的节点样式在条件分支场景下可读性更好。我的建议是直接用flowchart因为它是目前主推的写法。节点形状和连线方式是最需要记住的基础部分A[文本]表示矩形节点A{文本}表示菱形判断节点A((文本))表示圆圈节点A -- B表示实线箭头A -.- B表示虚线箭头A -- 标签 -- B给箭头加文字说明。子图subgraph是控制复杂度的利器。例如把订单处理拆成校验阶段支付阶段履约阶段三块整体结构会清晰很多flowchart LR subgraph 校验阶段 A[校验参数] -- B[校验库存] end subgraph 支付阶段 C[创建订单] -- D[调用支付] end B -- C D -- E[完成]写代码时有一个很隐蔽的错subgraph的标题如果包含空格必须用引号包起来比如subgraph 支付阶段否则某些渲染器会把空格后面的内容当成节点 ID直接导致布局错乱。2.2 时序图描述对象间消息交互的首选比起流程图时序图更贴合后端和前端协作时的表达需求。Mermaid 的时序图用sequenceDiagram关键字开启核心元素是参与者和消息箭头。sequenceDiagram participant U as 用户 participant S as 服务端 U-S: 发起请求 S--U: 返回结果这里的-表示带箭头的实线--表示带箭头的虚线通常语义是调用/响应。和-还能表达激活和释放调用开始后对参与者区域加高亮块结束时再关闭读图时会非常直观地看出某个方法在什么时间段处于占用状态。时序图在文档里的最大价值是不需要再去画那种丑丑的方块之间连几根线来示意接口调用。哪怕是一次简单的登录流程用两三行文本就能表达清楚而且方法名、参数、返回结果都在图里配合代码注释使用几乎零维护成本。2.3 甘特图和状态图被大多数文档忽视的两个类型很多人一提 Mermaid 只想到流程图和时序图但实际上甘特图、状态图在实际文档写作中也非常好用尤其是方案说明和项目排期。甘特图的核心是三要素日期格式、任务分区、任务起止时间。下面是我常用的模板gantt title 项目排期示例 dateFormat YYYY-MM-DD section 设计 接口定义: done, 2024-01-01, 3d 联调方案: 2024-01-05, 5d section 开发 模块A开发: 2024-01-10, 7d 模块B开发: 2024-01-12, 7d用section把不同职责的任务分组done表示已完成后面的3d是持续时长。把这份排期放进技术方案文档里哪怕团队没有任何项目管理工具读者也能一眼看到里程碑和时间冲突。状态图用的是stateDiagram-v2关键字适合表达业务单据的状态流转。例如一次审批从待提交到已通过中间可能有退回和终止stateDiagram-v2 [*] -- 待提交 待提交 -- 审核中 审核中 -- 已通过 审核中 -- 已驳回 已驳回 -- 审核中 审核中 -- [*]这类图在需求评审中特别有用光靠文字描述状态流转容易漏分支画成状态图后缺失的状态转换一望便知。2.4 从文本到图表的解析规则容易忽略的基础不少新手的第一个困惑是为什么看起来完全一样的代码在 A 平台能渲染到 B 平台就变成了原始文本答案多半出在代码块语言标识上。Mermaid 图表必须放在标注了mermaid语言的代码块里如果只写了text或者没有语言标识很多 Markdown 渲染器只会显示出纯文本。还有三个容易被忽略的解析细节节点 ID 和标签是两个概念。A[内容]中 A 是 ID内容才是显示文本。ID 不建议使用空格、括号、斜杠等特殊字符否则解析器会把 ID 的部分内容误当作新节点。箭头标签如果包含中文标点尽量用引号包住整个标签例如-- 检查通过 --避免全角逗号或括号干扰解析。%%是注释符号注释内的内容不会参与渲染。我会在每张图的头部写一段注释说明这张图的业务背景这个习惯后面还会再讲。3. 把 Mermaid 请进日常文档工作流3.1 编辑器和预览环境的最低配置想在本地顺畅使用 Mermaid最省事的方式是找一款内置支持的 Markdown 编辑器。目前主流的几款常见 Markdown 编辑工具都提供了 Mermaid 渲染能力但有的默认关闭需要在编辑器偏好设置里手动开启图表或Mermaid选项。我最初就栽在这上面搭建好环境后预览窗口一片空白还以为是语法写错了后来才发现功能开关没打开。如果编辑器不支持内建渲染还有两条路径本地临时验证用官方在线编辑器粘贴代码边改边看渲染结果自动化出图写一个命令行脚本用 Node.js 调用渲染库把.md文件里的 Mermaid 代码块批量导出成 PNG 或 SVG再嵌入文档站点。我现在的做法是编辑体验和构建产物分离平时在编辑器里写文本直接看预览效果到了要交付最终文档或幻灯片时再通过脚本批量导出图片。这样省心也不会受单一平台限制。3.2 静态站点与团队知识库的集成思路团队内部的知识库通常搭在某个文档站点生成器上Markdown 文件放在代码仓库里提交后自动构建发布。在这种体系里接入 Mermaid大多数情况是在站点渲染管线上增加一个插件或组件把 Markdown 中语言标识为mermaid的代码块交给渲染引擎而不是当作普通代码展示。我遇到过的一次具体情况是图插进文档站后网页上什么都没显示只有一堆源码文本。排查后才发现问题不在 Mermaid 语法而是在构建管线里代码块内容被 HTML 转义钩子提前处理箭头符号--里的被转成了 HTML 实体渲染引擎就解析失败了。解决办法是在处理环节把 Mermaid 代码块过滤出来跳过常规转义先解析再渲染。另外要特别提一下版本一致性问题。Mermaid 自身迭代速度不算慢不同版本对某些新语法的支持程度不一样。如果在文档站构建环境里装的是旧版本而本地编辑器用的是新版本就会出现本地能渲染、线上渲染不了的情况。我会在所有使用场景里尽量固定同一个解析器版本升级之前先把文档库里所有图批量跑一遍防止某个新关键字在旧版本里直接翻车。3.3 图表即代码版本管理与评审效率从我的观察看图表即代码带来的最大变化不在绘制环节而在评审环节。以前我在评审会上一张一张解释架构图涉及改动时大家口头讨论会后靠人工记忆修改现在每个人可以直接在文档源码里看到图对应的文本改动 diff评审粒度细化到具体哪条连线、哪个节点变化了。举个例子一个接口调用链路图从 5 个节点改成 7 个节点代码评审时你看到的不是一张模糊的截图对比而是清晰的文本增删记录。配合注释说明review 者能立刻判断改动动机是否合理。这种协作体验是传统绘图工具很难复刻的。还有复用价值。流程图里的通用步骤比如参数校验 - 鉴权 - 业务处理 - 返回结果完全可以从一个文档复制到另一个文档只需要改业务细节。我甚至会把一些固定的图模板放进团队公共知识库里新同事写方案时直接套用比自己从零画一张图规范得多。4. 我在实际使用中踩过的坑与排查过程4.1 渲染失败最常见的原因语法报错只是表象有次我把一段流程图从在线编辑器搬到项目文档里渲染出来是空白连错误提示都没有。当时我的第一反应是检查语法但反复核对感觉没问题于是我把代码原样粘贴到官方解析器里竟然又能渲染。这一下就把范围锁定了代码本身没问题问题出在渲染环境。排查链路大概是这样检查代码块语言标识是否正确。这个最基础但很多人漏掉。复制代码到官方解析器或另一个编辑器里确认代码本身能否渲染。如果代码能渲染而目标环境不行去找渲染管线里的转义、插件、缓存问题。如果代码和渲染环境都正常但还是失败就要考虑是不是固定语法没被旧版本解析器支持。那一次最终定位是文档站的渲染器版本太旧不支持我使用的某类节点样式写法。弄清楚原因后我的应对策略是给站点固定解析器版本同时养成不用太激进的语法的习惯确保图和文档环境的兼容性。排查问题最重要的一步不是背下所有错误提示而是能够把问题快速划分到语法问题、环境问题、版本问题三个桶中的某一个。4.2 中文内容与特殊字符看着没问题解析起来全是问题中文用户最容易踩的坑不在关键字而在标点符号。有个很典型的例子flowchart TD A[判断是否通过] -- B{是否通过} B -- 是通过 -- C[继续] B -- 否拒绝 -- D[结束]这段看起来没问题但箭头标签里的中文逗号和后面的文字组合在某些解析器里会引起歧义。更稳妥的写法是用引号包住标签B -- 是通过 -- C[继续]除了标点符号字段名里如果包含/、(、)这类字符也需要特别小心。我在画数据流转图时吃过一次亏某个字段叫A/B直接写在节点文本里导致连线被拆断。解决方式是把节点文本用引号包裹写成A[字段A/B]解析器就会把整段当作一个整体。还有一个很实用的小技巧如果节点文本太长在 Mermaid 里可以用br/标签强制换行。否则节点会被无限拉宽整张图看起来非常笨重A[这是一段很长的文字br/需要分成两行显示]4.3 布局失控与方向调整图丑的根源往往不是配色Mermaid 自动布局很省心但遇到复杂图它默认的排列顺序不一定符合人类的阅读习惯。最典型的例子是画一个横向的业务流程节点很多箭头来回横跳最后渲染出来像一团毛线。问题通常出在图的方向选择上TD表示从上到下LR表示从左到右选错方向会让整个图的可读性大幅下降。还有一个困扰过我的问题是subgraph嵌套。子图之间如果有交叉连线布局引擎的处理结果可能让人困惑节点明明属于 A 区却被推到 B 区附近。后来我总结出一个规律子图内的节点和子图之间的连线尽量保持先内部后外部的组织顺序连线的起点终点都明确落在子图边界上布局的可预测性会好很多。对于图丑我的最终解决方案是提前分层而不是事后微调。一张超过 15 个节点的流程图我会在写文本之前先在注释里规划好哪些节点属于主流程哪些属于异常分支哪些需要放进子图。文本结构顺了渲染出来的图基本不会太难看。5. 主题配置与进阶技巧让文档里的图不只是能用5.1 用主题变量统一风格默认主题看久了难免审美疲劳尤其当文档里同时存在多张图配色五花八门会显得很不专业。Mermaid 提供了几个内置主题default、neutral、dark、forest。技术文档里最保险的是neutral整体灰白调子和多数界面融合度高dark适合深色背景的站点forest偏绿色调适合带自然元素的展示场景。如果团队有品牌色要求可以覆盖主题变量mermaid.initialize({ theme: neutral, themeVariables: { primaryColor: #f0f4f8, primaryTextColor: #1a202c, fontFamily: monospace } });这段配置的意思是以neutral为基础重新定义主要背景色、文字色和字体。这样整份文档里的流程节点、时序图背景都会跟着变而不需要每张图单独加样式指令。配置的变量名以官方文档为准建议先在一张测试图上调好色再全局应用。5.2 从官方示例到二次封装沉淀团队图表模板单独用 Mermaid 和把 Mermaid 用成团队规范差着一层模板封装。我比较推荐的做法是维护几个标准图模板文件把固定结构写好业务字段留成待替换位置。比如服务调用时序图模板sequenceDiagram participant C as 客户端 participant G as 网关 participant S as 服务端 C-G: 请求 G-S: 转发 S--G: 响应 G--C: 返回之后新人要画类似图只需要替换参与者名称和消息内容既保证了画风统一也减少从空白开始的时间。模板本身也放进版本库里谁都能维护。更进一步我还会在文档里给每个图配一段文字说明。图的旁边写清楚这张图描述什么、关键路径是什么、为什么这样设计这样即使渲染失败或者浏览环境不支持读者也能从文本中读出图的内容至少不会完全损失信息。5.3 几个值得长期维护的实用习惯如果只能用一句话总结我这些年用 Mermaid 的体会那就是把它当成源码来维护而不是当成图片来管理。图中必须写注释。用%%标注图的业务背景、责任人、修改意图。没有注释的图三个月后连自己都看不懂。尽量同文档一起提交。图和说明文字放同一份 Markdown 里不要分开存放否则很快会失去同步。导出图片只在交付时做。日常写文档不需要一次次导出 PNG预览够用就行。升级渲染器前先做全库回归。遍历所有 Markdown 文件里的 Mermaid 代码块确认没有出现不兼容的新语法或废弃关键字。我现在最常用 Mermaid 的场景反而是一些看起来很不起眼的过程说明一次发布步骤、一个数据流转链路、一段权限判断流程。越是这样容易被忽略的文档越需要一张清晰的图让接手的人能三秒进入状态。文本绘图不完美它依然有布局控制和视觉表达的局限但它真正改变了画图和改图这两个动作的关系——改图不再是一件让人头疼的返工而只是顺手修改几个字的常规操作。