我第一次接触 Mermaid 语法是在一次方案评审前同事把流程图丢进PPT我为了改一个判断分支整整折腾了二十分钟。从那时候起我就意识到凡是图比文字贵的地方一定需要一种能用文本描述图表的方式。Mermaid 就是干这个的——它用类似代码的简练语法画流程图、时序图、状态图、类图渲染出来是清晰的 SVG 图片底层却是纯文本天然支持 diff、版本管理和多人协作。如果你写技术方案、维护接口文档、做个人知识库或者只是想让会议上的逻辑图不再每次重画Mermaid 都值得花半小时入门。这篇文章我会从最基本的 graph 语法开始一路拆到 CLI 渲染和自动化校验把我实际踩过的坑一并讲清楚。1. 为什么我在所有文档里都改用 Mermaid 画图1.1 从一段画完就没人维护的流程图说起以前画流程图我最常用的工具是 Draw.io 和 PowerPoint。图形界面的问题不在于画不出来而在于画完就没人愿意改。一张图放在文档里过两周业务规则变了想找到源头文件都麻烦更别说让多个同事同时编辑。最痛的一次是核心流程改动后图还停在旧逻辑上评审会上被当场指出场面非常尴尬。Mermaid 把画图变成了写代码。节点用A[开始]连线用--整张图就是一个纯文本文件。你不需要拖动方块不需要对齐箭头只需要在编辑器里改一个字符。因为内容是文本用 Git 管理时能看到每一行变化同事 review 流程图就像 review 代码一样指出第 7 行的判断条件不对就行。这种含金量是图形工具给不了的。所以我的使用场景很明确架构设计、接口调用链、发布流程、故障排查手册、个人笔记。凡是可能被反复修改的图我一律用 Mermaid只有面向客户演示、需要精致排版的少数场景才会导出 SVG 后再用图形工具补一层。Mermaid 的目标不是取代所有画图软件而是让图回归到信息表达本身。1.2 Mermaid 与语法这件事的关系严格说起来Mermaid 不是编程语言而是一种领域特定语言DSL。它的语法范围被刻意控制在描述图表这个领域里关键字不多没有变量作用域没有函数调用学习成本很低。你只需要记住节点怎么声明、连线怎么连、不同图类型有哪些专属关键字就够了。它与我们熟悉的 Markdown 语法是天然的搭档。Markdown 负责排版文字Mermaid 负责插图。GitHub、GitLab、Hexo、VuePress、Docusaurus、Typora、Obsidian这些常见平台都已经内置或通过插件支持 Mermaid。也就是说你在写 README、Wiki、接口文档时不需要把图片文件单独存一个目录只要把 Mermaid 源码放在 Markdown 的围栏代码块里页面打开时自动渲染成图。很多从编程语言过来的人会把 Mermaid 的简写记法称为语法糖。比如A -- B代表一条带箭头的连线A --- B代表一条无箭头连线A 文字 B代表一条加粗且带文字的连线。这些符号本质上都是完整写法的简化形式和你在代码里用for循环省略步进变量一样看着简单表达能力却不弱。对不会编程的人来说也不用怕你不需要理解什么叫 AST只需要把语法当作画图的约定符号来记就像记住交通标志一样自然。1.3 解析思路看着像文本其实是 AST我第一次遇到 Mermaid 报错时第一反应是这也能报错。后来查了一下它的实现才明白 Mermaid 并不是把文本直接翻译成 HTML它要经过一个真正的解析过程先做词法分析把文本切分成关键字、括号、引号、ID、箭头等 token再根据当前图类型生成一个抽象语法树AST最后再由渲染器遍历这棵树输出 SVG。这也是为什么 Mermaid 能在浏览器和 Node.js 环境里共用同一套语法——图和平台无关只和输入的文本结构有关。理解这一点对你排查语法问题特别有帮助。比如graph TD里graph是图类型关键字TD是方向关键字它们之间的顺序不能乱再比如A[开始]中A是节点 ID[开始]是显示文本方括号在语法树里就是一个形状标记你用成中文全角括号【】解析器就认不出来了。还有缩进Mermaid 不是靠缩进确定结构的语言但在subgraph子图内部缩进可以大幅提升可读性如果缩进混乱导致end不匹配渲染结果会差很远。所以遇到报错别急着怀疑工具先看它提示的行号和 token再去对照官方关键字。大部分 Mermaid 报错本质都是你写的东西不在语法树的设计范围里。2. 流程图语法拆解节点、连线、子图的每一处细节2.1 最小的 graph 声明为什么方向关键字不能随便写流程图的 Mermaid 语法从graph开始后面跟着方向关键字。常见的有TB从上到下、TD从上到下和 TB 等价、BT从下到上、LR从左到右、RL从右到左。如果你不写方向默认也是从上到下排列但建议还是显式写清楚因为方向影响布局布局又会直接影响别人对流程的理解。一个最简单的图只需要三行文本graph TD A[开始] -- B{是否就绪?} B -- 是 -- C[执行任务] B -- 否 -- D[等待]在支持 Mermaid 的编辑器里把这段源码放进围栏代码块并标记为 mermaid 语言就能看到开始节点指向一个菱形判断判断分两条路径流出。这里A、B、C、D都是节点 ID方括号内是显示文本花括号代表判断节点。刚开始学的时候最容易犯的错是把方向写成TR或DT这类错误解析器会直接抛Lexical error而且报错位置很可能离真实问题不远。节点 ID 的命名也要养成习惯。虽然 Mermaid 允许中文 ID但建议统一用英文 ID 加中文标签例如user[用户]、server[服务端]。因为后面你可能会给节点加样式、绑定点击事件甚至用脚本生成文档英文 ID 在自动化处理时远比中文可靠。不规范命名的代价在只有几行的小图里不明显一旦图超过二十个节点就会混乱到连自己都分不清。2.2 节点形状的语法糖用最少字符表达最清晰的结构Mermaid 节点形状是一组很容易记的语法糖用不同的括号包裹显示文本就能切换形状。下面是我自己经常用的对照表写法渲染形状常用语义A[文本]矩形普通步骤、动作A(文本)圆角矩形也可表示步骤或温和的开始/结束A((文本))圆形开始、结束、入口、出口A{文本}菱形判断、条件分支A[[文本]]子程序调用外部流程A[/文本/]平行四边形输入、输出、数据A文本]不对称矩形请求方、客户端角色我实际用下来的体会是不要贪多。大部分流程图只需要三种形状——矩形表示步骤、菱形表示判断、圆形或圆角表示开始结束。其他形状用于特定场景比如数据流图用平行四边形架构图用子程序样式。全图花里胡哨反而失去重点。团队里如果还没有约定我建议直接在文档规范里写死业务流程图只用矩形 菱形 圆形三种其余形状杜绝滥用。另外形状括号必须成对出现而且要在英文半角状态下输入。中文【文本】和英文[文本]在屏幕上看着相似解析结果完全不同。这个坑我踩过很多次尤其在习惯使用中文输入法的情况下。如果你发现图渲染不出来先检查所有括号是不是半角。2.3 连线不只有箭头实线、虚线、粗线、带文字流程图的连线语法是 Mermaid 里变体最多的地方。最基本的--是实线箭头表示流程从上一个节点流向下一个节点---是无箭头实线表示关联但不强调方向-.-是虚线箭头我通常用来表示可选的、异步的、消息类的流转是粗线箭头表示主路径或者强依赖关系。具体选哪种没有绝对标准关键是整个图要保持一致否则读者会困惑。给连线加文字是最常见的需求。写法有好几套例如graph LR A[提交订单] -- 包含商品 -- B[库存校验] B -- 库存不足 -- C[提示失败] B -- 库存充足 -- D[扣减库存]这里-- 包含商品 --表示实线箭头加文字标签。你也可以写成A --|包含商品| B两种方式都能渲染出相同效果我比较推荐后者因为它在处理复杂长文本时更容易对齐。虚线加文字则是A -. 可选步骤 .- B粗线加文字是A 主路径 B。还有一个容易被忽略的写法是链式连线A -- B -- C。它等价于A -- B和B -- C两条线节点 B 会自动出现在中间。这种写法在流程简单时很清爽但我建议不超过五六层否则中间节点很难插入分支。分支多的时候还是老老实实写成一行的两个判断清晰度远胜一条线串到底。2.4 子图和样式类让大图不至于失控当流程图节点超过二十个最有效的整理手段不是调字号而是子图subgraph。它的作用是把一组节点圈进一个容器视觉上形成模块分区。语法如下graph TD subgraph 订单服务 A[创建订单] -- B[支付] B -- C[发货] end subgraph 物流服务 D[揽收] -- E[运输] E -- F[签收] end C -- D子图可以嵌套也可以带 IDsubgraph s1[订单服务]。这里要特别提醒子图标题如果直接跟在subgraph后面Mermaid 会把它当作 ID再看后面的空格当作显示文本容易产生歧义。我习惯始终显式写成subgraph s1[标题]这种带 ID 的形式避免解析问题。节点样式方面Mermaid 支持style和classDef。classDef类似于给一组节点定义一个样式类再用class 节点ID 类名去应用也支持在节点定义时直接加:::类名。比如graph TD classDef ok fill:#e6ffed,stroke:#2da44d,stroke-width:2px classDef warn fill:#fff8c5,stroke:#d4a72c,stroke-width:2px A[完成]:::ok -- B[重试]:::warn这套写法和 CSS 的思路很像。它不是你画完图之后需要用鼠标一点点调色而是在文本里声明好哪些节点是什么语义。颜色在这里不只是装饰它传递状态绿色代表正常黄色代表需要注意红色代表错误。团队一旦接受这套约定评审流程图的时候就不需要一张张问这个节点什么含义。2.5 从 pipeline 脚本语法看 Mermaid文本即结构如果你写过 Jenkins Pipeline 或者 GitLab CI Pipeline你会发现 Mermaid 的思路和 pipeline 脚本语法出奇地一致。Pipeline 用stage、steps、when把一段部署流程结构化Mermaid 用graph、subgraph、节点和箭头把业务流程图结构化。两者都是用文本描述过程都强调层级和顺序也都支持条件分支。我见过不少团队Jenkins 的 pipeline 脚本写得规规矩矩一到画架构图就回到 PPT。其实完全可以把写 pipeline 的思维搬过来先画骨架把大阶段列出来再往每个阶段里补节点。比如一次发布流程如果写成 pipeline 大概是构建 - 测试 - 部署 - 验证四个 stage对应到 Mermaid 里就是一个从左到右的graph LR每个 stage 用一个subgraph包起来stage 内部再写具体步骤。这种转换几乎没有学习成本只需要记住 Mermaid 的关键字就行。从更广的角度看GitHub Actions 的job/steps、Docker Compose 的services、Ansible 的tasks这些都是同一种思维模型。你越熟悉任何一种声明式流程语法学 Mermaid 就越快。反过来Mermaid 也能帮你直观检查这些脚本里的依赖关系——把脚本里的步骤节点和连线画出来阶段之间的依赖看不清楚的问题往往一目了然。3. 时序图、状态图、类图的差异化语法别把四种图写成一种形状3.1 时序图参与者、消息箭头、激活框流程图适合表达分支和决策但如果你要表达的是多个角色之间按时间顺序发送消息时序图是更准确的选择。Mermaid 的时序图由sequenceDiagram开头参与者用participant声明消息用箭头表示。一个最小例子是这样sequenceDiagram participant U as 用户 participant S as 服务端 U-S: 发起请求 activate S S--U: 返回结果 deactivate S这里participant U as 用户把参与者的 ID 设为 U显示名设为用户。U-S是实线箭头表示同步请求S--U是虚线箭头表示响应。activate S和deactivate S配对使用会在服务端生命线上画出激活框直观展示处理时长。箭头方向是从发送方到接收方千万不要写反否则整张图的消息顺序会误导人。时序图的箭头类型还有几种-)表示异步消息--x表示带叉号的失败响应-)表示异步完成。我在画接口调用链时习惯用-表示 HTTP 请求用--表示响应用-)表示 MQ 这种异步消息。Note left of U、Note right of S可以给参与者加解释性备注适合标注超时、重试、缓存等补充信息。时序图最常见的坑是activate和deactivate不配对。如果激活了服务端后面忘了释放渲染出来的图就会多出一段奇怪的竖条。另一个坑是参与者 ID 重名或大小写不一致Server和server会被当成两个不同的参与者。命名规范越早定后面维护越省心。3.2 状态图先把有限状态机想清楚状态图stateDiagram-v2是我在订单流程、审批流程、前端页面状态里最喜欢用的图。它本质上就是有限状态机每个节点是一个状态箭头是一个迁移事件箭头上的文字是触发条件。示例stateDiagram-v2 [*] -- 待处理 待处理 -- 处理中 : 开始 处理中 -- 已完成 : 成功 处理中 -- 待处理 : 失败回退 已完成 -- [*]这里的[*]是特殊的初始状态和终止状态。第一次写状态图的时候我把它当普通节点处理结果渲染出来的图里多了一个带中括号的奇怪节点。正确的做法是初始状态用[*] -- 状态结束状态用状态 -- [*]普通状态之间用--连接迁移条件用冒号跟在箭头后面。状态图最值钱的地方在于它逼你先想清楚状态有哪些、什么条件下跳到哪个状态。很多人把状态图画成了流程图满图都是判断菱形其实状态机的核心是当前状态遇到事件后迁移到新状态而不是怎么一步步执行。举个订单例子待支付、已支付、已取消、退款中、已退款这些是状态支付成功用户取消超时关闭是事件状态图展示的是事件触发后的状态转移而不是整个支付接口的调用流程。Mermaid 的状态图还支持并发状态--和组合状态不过日常使用中先把有限状态机的五种状态和四条迁移线画清楚比堆叠复杂语法更实用。注意版本差异旧版是stateDiagram新版推荐stateDiagram-v2后者的语法更严谨渲染样式也更统一。3.3 类图用 UML 关系记录领域模型类图是所有 Mermaid 图里最像编程的一种。它用classDiagram开头类内部可以声明属性、方法类之间可以声明继承、组合、聚合、依赖等关系。一个最小例子classDiagram class Animal { String name move() } class Dog { bark() } Animal |-- Dog这里Animal |-- Dog表示Dog 继承 Animal箭头指向父类。属性和方法前的表示公有-表示私有#表示保护。如果你不关心访问修饰符也可以直接写String name和move()但养成标/-的习惯之后图能承载的信息量会大很多。类图关系符号还有*--组合、o--聚合、--关联、..依赖。组合关系表示整体与部分同生共死聚合关系表示可以独立存在依赖关系表示临时使用。别把这些符号混用否则领域模型会传达错误语义。类图很适合做一次小型的领域建模。比如你在设计一个会员系统先用类图把用户、订单、优惠券的关系画出来再交给后端写代码。Mermaid 的类图不是代码生成器它的价值在于把领域对象和关系落到文本里方便评审和迭代。用 C 结构体链表、Java 类这类概念来类比也成立类图上的move()就是方法签名-String name就是私有字段语言本身是什么并不重要关键是你把结构表达清楚了。和类图思维更接近的还有图数据库的 Cypher 基本语法。Cypher 用MATCH (a)-[r]-(b)表达节点和关系Mermaid 类图也天然是节点 关系线。我记得第一次看到MATCH (user)-[:PAID]-(order)时脑海里自动就浮现出一张类图user和order是两个节点PAID是关系。语法不同底层思维一致。3.4 一张语法速查表比什么都管用网上各种语法大全很多从正则表达式语法大全到 nmap 参数表本质上都是给工具使用者提供一个索引。Mermaid 也值得你维护一张自己的速查表。不用多复杂四行就能概括大部分需求图类型起始关键字核心要素最常踩的坑流程图graph TD/LR节点、连线、判断、子图方向关键字写错、中文括号时序图sequenceDiagram参与者、消息箭头、激活框activate/deactivate 不配对状态图stateDiagram-v2状态、迁移事件、初始结束把 [*] 当普通节点类图classDiagram类、成员、关系符号泛化箭头方向画反这张表不是让你背下来而是让你在每周只有零星几次画图需求时不用每次都翻完整本官方文档。从我自己的经验看流程图用量最大时序图和状态图次之类图在专门做领域设计时才有存在感。刚开始学的时候先把流程图练熟再扩展其他图类型不要一次把四种图的语法全塞进脑子里。我也见过有人搞出markdown 语法 谷歌语法混合检索甚至想用搜索语法查找学号这类个人信息。这里必须说清楚搜索引擎的布尔语法确实强大但任何以获取他人隐私为目的的用法都越过了边界不光是合规问题更是基本尊重。Mermaid 也一样语法本身是工具怎么使用完全取决于你的意图别把检索能力用在错误的地方。4. 从能出来图到敢放进生产文档命令行渲染、自动化校验与排错4.1 不要只依赖编辑器命令行渲染才是自动化基础Mermaid 的源码在 Obsidian、Typora 里都能直接预览但你要是只想看图很容易陷入一个误区图只有自己打开编辑器才能看到。真正要把 Mermaid 用进生产环境你需要一个命令行工具mermaid-js/mermaid-cli装好后一般提供mmdc命令。基本用法是这样npx -y mermaid-js/mermaid-cli -i demo.mmd -o demo.svg这条命令会把demo.mmd渲染成demo.svg。你也可以输出 PNG、PDF比如-o demo.png需要时用-w 1200 -s 2控制宽度和缩放倍数。主题切换用-t default、-t dark、-t forest我习惯在文档站里统一使用某个主题避免不同文章截图风格不一致。为什么命令行重要因为你的文档流程可以自动化了本地写完.mmd文件跑一遍命令批量生成图片再把图片提交到文档站点整个过程不需要打开图形界面也不需要任何人手工截图。这也就意味着流程图可以放进 CI 流程文档变更时自动渲染、自动发布。市面上所谓破解版、高级版下载完全没必要碰Mermaid 和它的 CLI 都是开源免费的官方文档写得比任何二手教程都清楚用最新稳定版就够。有一点要提前有心理准备mermaid-cli 依赖于浏览器内核来渲染。首次运行可能要下载一个无头浏览器如果你的环境比较特殊下载可能比较慢或者需要配置离线缓存。解决办法通常是提前在构建机把浏览器装好然后再运行 CLI。这不是 Mermaid 的问题而是所有浏览器渲染方案都逃不开的环节。4.2 用 Playwright 做批量校验杜绝图渲染出来但节点是错的命令行解决了生成图片但还没解决图里的内容是否正确。我们曾经遇到过一个场景文档里二十多张 Mermaid 图某次改动后大部分能渲染但几张小图的节点名被复制粘贴错了文字层面完全看不出来。后来我引入 Playwright 对渲染后的页面做批量断言问题才被系统性地拦住。Playwright 是浏览器自动化框架适合做这类文档级校验。思路是把 Mermaid 源码渲染进一个 HTML 页面等待 SVG 生成然后截图并检查 SVG 里是否存在关键文本。一段最小脚本大致长这样const { chromium } require(playwright); (async () { const browser await chromium.launch(); const page await browser.newPage(); await page.goto(file:///path/to/rendered-doc.html); await page.waitForSelector(svg); const content await page.textContent(svg); if (!content.includes(订单服务)) { throw new Error(图中缺失关键节点订单服务); } await page.screenshot({ path: diagram.png, fullPage: true }); await browser.close(); })();这个脚本会打开本地 HTML等待 Mermaid 渲染出的svg出现然后取整个 SVG 的文本内容判断关键节点是否存在。你还可以把目录下所有 HTML 文件都跑一遍发现任何一张图缺节点就报错这样就不至于把错误图直接发布上线。截图拿到后再配合 diff 工具比较不同版本的图很容易定位这次改动影响了哪些图。Playwright 的能力不止于此你还可以用它模拟鼠标悬停、点击 Mermaid 节点的绑定事件甚至把渲染错误捕获下来。不过对大部分文档团队来说先做到自动截图 自动断言关键节点已经足够。自动化不是要把事情变复杂而是把最重复、最容易被忽略的检查交给脚本。4.3 常见语法报错和排查思路别一次改一整段Mermaid 的报错信息这些年已经很友好但仍然有几种高频问题值得单独拿出来说。第一方向关键字写错。比如graph TR正确的是TB、TD、BT、LR、RL。这个错误经常出现在从别人代码里复制时方向没改全。第二括号不匹配。中英文括号混用是灾难源A[开始]里的方括号必须是半角A[开始会导致解析器在期望]时遇到。第三subgraph少了end。子图开了一个结尾没有end后面的节点全部被套进子图布局全乱。第四节点 ID 使用了特殊字符比如.、#、它们在部分图类型里会被当作语法符号轻则渲染警告重则直接失败。排查这类问题我推荐的链路是最小化复现 二分注释。先删掉一半节点和连线看问题是否还在如果不在再还原一半逐步缩小区间。不要一次改一整段因为你不知道到底是哪一行引起的改完可能另一个问题浮出来。报错信息里的行号非常关键但它的含义在不同图类型里略有不同有时候行号指向的是解析停止的位置并不一定就是真正有问题的位置。这时候需要你把可疑行的每一个关键字、括号、空格都和官方示例比对。版本差异也要注意。Mermaid 的 v9、v10、v11 之间某些图的语法和主题系统有变化。网上搜到的教程很可能基于旧版本照抄时如果渲染结果不对先看官方文档对应的版本。我个人遇到最多的是状态图老教程写stateDiagram新版本更推荐stateDiagram-v2看起来只差一个后缀实际行为差很多。与其纠结不如把项目的 Mermaid 版本锁死README 里写明本仓库使用 v10.x避免队友用了不同版本来渲染同一份源码。4.4 复用模板比背语法更重要我在实际项目中踩过不少坑之后最大的体会是Mermaid 真正提升效率的方式不是背熟所有语法而是建立一套自己的模板库。比如我会在本地维护一个mermaid-templates目录里面放着常用图的骨架flow-basic.mmd基本流程图包含开始、判断、结束节点sequence-api.mmd时序图包含参与者、同步请求、响应、异常分支state-order.mmd订单状态机包含待支付、已支付、退款等状态class-domain.mmd类图包含实体、值对象、聚合和关联关系每次新项目需要画图复制对应模板改节点名和连线即可。这样既保证风格统一又不需要每次重新敲一遍基础结构。模板里的样式类classDef也尽量固定下来绿色表示完成、黄色表示等待、红色表示失败团队成员看多了就形成条件反射一眼扫过就知道整张图的状态分布。另外一个小技巧给.mmd文件取名字的时候不要叫未命名1.mmd用2025-05-订单支付流程.mmd这类带日期和业务名的格式。文件一旦多起来你会感谢当初这个习惯。图文文档不是画完就结束的一次性工作它是一个持续演进的资产命名规范、模板统一、版本管理这三件事比任何花哨的语法都重要。最后再分享一个长期养成的习惯画图之前先在纸上或者直接在文本里列一下节点清单和连线清单字段也不复杂就是从谁到谁、线代表什么关系。很多时候画图卡的其实不是语法而是你自己没想清楚逻辑。Mermaid 的文本特性反而是一种约束它逼你把流程是什么写得明明白白而不是靠鼠标拖动慢慢找感觉。语法只是表达真正值得花时间的永远是你要表达的那件事本身。