Mermaid基础入门:从第一张流程图到时序图
事情是这样的。上周我想给一篇旧博客补一张流程图,那张图半年前在 ProcessOn 里拖过,这次要加一个节点,连线的位置全乱了,拖了十几分钟没弄好,最后干脆删掉重画。晚上躺床上我就在想,为什么画个图这么累。
图表达的是关系,关系用文字就能说清楚,可我偏偏要用鼠标在一个画布上一点点摆位置。这不合理。
后来我换成了 Mermaid。第一次把一段文本粘进文档,自动渲染出一张整齐的流程图时,感觉跟第一次用 Markdown 写东西差不多,原来还能这样。
这篇是 Mermaid 教程的上篇,目标很具体,让你会用 flowchart 画流程图,会用 sequenceDiagram 画时序图。下篇再补上类图、状态图、ER 图和主题工程化。
Mermaid 是什么
Mermaid 是用文本画图的工具。你写一段描述关系的代码,它渲染成一张图,代码放进 Markdown,发布的时候图就自动出现了。
它的名字取自美人鱼,我第一反应这是某个绘画软件,其实它是 JavaScript 写的开源库,核心是一个叫 dagre 的布局引擎。你要做的只是描述谁连向谁,怎么摆,引擎说了算。
这句话值得展开。用鼠标画图,你管理的是位置,图一复杂,位置就成了最大的负担。用 Mermaid,你只管理关系,位置交给算法。想改图就改文本,改动可以进 Git,可以被 diff、被评审,跟改代码一个待遇。
为什么选 Mermaid
说实话,肯定有人想,我用 Visio 用得好好的,为什么要换。这个问题的答案,取决于你的图活在哪里。
如果你跟我一样,图的用途是写博客、写技术方案、写内部文档,那图的命运就是跟着文字一起迭代。文字在 Git 里,图却躺在某个在线工具的账号里,这是割裂。Mermaid 让图和文字活在同一个文件里,文档更新,图跟着更新。
| 方案 | 改图成本 | 能否进 Git | 适合场景 |
|---|---|---|---|
| Visio / ProcessOn | 拖拽调整,复杂图易乱 | 否,图是文件 | 给客户看的精美交付图 |
| 手绘 / 截图 | 推倒重画 | 否 | 一次性草图 |
| ASCII art | 对齐到崩溃 | 勉强可以 | 终端示意图 |
| Mermaid | 改一行文本 | 可以,代码即图 | 文档、博客、技术方案 |
但也要承认边界。给客户看的像素级精美的架构图,Mermaid 给不了,它没有那种精细控制,布局是引擎说了算的。我自己也还是会在某些场合打开 ProcessOn。工具不是信仰,场景才是。
环境准备
上手 Mermaid 不需要安装任何东西,从在线编辑器开始。
mermaid.live 是官方编辑器,左边写代码右边出图,实时渲染,零配置。我建议先把语法在这上面跑熟,再进编辑器。
VSCode 装一个 Markdown Preview Mermaid Support 插件,写 Markdown 时预览里就能直接出图。Typora、Obsidian 原生支持 Mermaid,打开即用。GitHub 的 Markdown 也支持,issue、PR、README 里的 Mermaid 都能渲染,这个对技术方案评审很实用。
要提醒一句,不是所有平台都认识 Mermaid。微信公众号、知乎这类编辑器会把代码块原样显示出来,变成一坨文本。遇到这种平台,把图导出成 PNG 再贴,mermaid.live 右侧有导出按钮,后面讲 CLI 也能批量导。这个坑我自己踩过,第一次往公众号搬文章,流程图全变成代码,当场愣住。
回到画图本身。语法才是重点,下面开始。
flowchart 基础语法
flowchart 是最常用的图表类型,没有之一。流程、判断、分支,都是它的主场。
节点
最小单元是节点,一行 A --> B 就画出一条从 A 到 B 的箭头:
对应源码:
graph TD
A[开始] --> B[写博客]
B --> C[发布]
第一行的 graph TD 是类型声明,TD 是 Top Down,从上往下。改成 LR 就变成从左往右,长流程用 LR 能省不少纵向空间。
节点可以用括号变形状,而且形状是有语义的:
对应源码:
graph LR
A[方框] --> B(圆角)
B --> C{菱形}
C --> D[(圆柱)]
D --> E((圆形))
方括号是普通方框,圆括号是圆角,花括号是菱形,常用来表示判断,圆柱一般代表数据库,圆形代表开始或结束。形状不只是好看,它给读者传递信息,菱形一出现,读者就知道这里有分支。这跟变量命名是一个道理,名字起好了,代码不用读注释。
连线
连线有几种形态,实线、虚线、粗线,语义可以自己约定:
对应源码:
graph TD
A[写完] -->|通过| B[发布]
A -.->|返工| C[修改]
A ==> D[记录到周报]
--> 是实线箭头,-.-> 是虚线,==> 是粗线。我的习惯是,正常流程用实线,异常或旁路用虚线,强调用粗线。箭头后面的文字用 |文字| 写在连线中间。
子图
图一长,就该用 subgraph 分组:
对应源码:
graph TD
subgraph 写作阶段
A[定题] --> B[写初稿]
B --> C[自检]
end
subgraph 发布阶段
D[排版] --> E[上线]
end
C --> D
subgraph 后面的英文名会显示成组的标题。子图是控制复杂度的第一层手段,图一旦超过 10 个节点,先想的不是怎么连线,而是怎么分组。这个原则后面讲复杂图拆分还会展开,先记住结论。
flowchart 画的是单个对象的流转。时序图画的是多个对象之间的消息往来,HTTP 调用这种场景,它天生合适。接着看 sequenceDiagram。
sequenceDiagram 基础语法
参与者与消息
对应源码:
sequenceDiagram
participant 浏览器
participant 服务器
participant 数据库
浏览器->>服务器: GET /posts/1
服务器->>数据库: 查询文章
数据库-->>服务器: 返回文章
服务器-->>浏览器: 200 渲染页面
participant 声明参与者,->> 是实线箭头,-->> 是虚线箭头。惯例是,同步调用用实线,异步或返回用虚线。箭头后面跟的是消息内容。
激活与生命周期
对应源码:
sequenceDiagram
participant C as 客户端
participant S as 服务端
C->>S: 请求
activate S
S-->>C: 响应
deactivate S
activate 和 deactivate 之间,参与者会多出一条竖条,表示它在这段时间里占用着资源。哪个对象在哪段区间忙,一眼就能看出来。排查性能问题的时候这个挺有用,竖条越长,阻塞越久。
分支与循环
真实系统里几乎不存在一条直线的调用,alt 和 loop 就是为这个准备的:
对应源码:
sequenceDiagram
participant 客户端
participant 网关
客户端->>网关: 请求
alt 登录态有效
网关->>网关: 放行
else 登录态过期
网关-->>客户端: 401
end
loop 重试最多三次
客户端->>网关: 重新请求
end
alt 是二选一的分支,else 是另一条路,loop 是循环。看到 alt 和 loop,你大概已经在想自己系统里的鉴权重试逻辑了。时序图的价值就在这,把代码里藏着的分支,画成看得见的东西。
博客发布流程实战
语法部分到这里就结束了,回到开头那个场景。把博客发布流程画出来,自检不过要返工,这就是一个带循环的流程图:
对应源码:
graph TD
A[写完初稿] --> B{自检通过}
%% 自检不过,回到 A 返工重写
B -->|否| A
B -->|是| C[排版配图]
C --> D[发布上线]
D --> E[分享到社群]
B 到 A 那条回头线,就是返工。用鼠标画图,这种回头线最折磨人,一拖全乱。在 Mermaid 里就是一行 B -->|否| A。
一次完整的博客阅读
最后来一个综合例子,把 sequenceDiagram 的能力用足。读者打开博客页面,浏览器、服务器、数据库之间发生了什么:
对应源码:
sequenceDiagram
participant 读者
participant 浏览器
participant 服务器
participant 数据库
读者->>浏览器: 点击文章链接
浏览器->>服务器: GET /posts/mermaid-tutorial
alt 文章存在
服务器->>数据库: 查询文章内容
数据库-->>服务器: 返回 Markdown
服务器-->>浏览器: 200 渲染页面
else 文章不存在
服务器-->>浏览器: 404
end
浏览器-->>读者: 显示文章
到这里,你应该能看懂这段代码,也能自己写出一张像样的时序图了。
小结
回顾一下这篇讲了什么:
- Mermaid 是文本即图表的工具,你写关系,它管布局,改图就是改文本
- flowchart 画流程图,重点在节点形状、连线形态、subgraph 分组
- sequenceDiagram 画时序图,重点在参与者、消息箭头、alt 和 loop
- 工具链从 mermaid.live 起步,VSCode 和主流 Markdown 工具都支持,注意平台兼容
- 图进 Git 能 diff 能评审,这是它跟拖拽画图最根本的区别
下篇讲剩下的图表类型,类图、状态图、ER 图、甘特图,以及怎么让图好看,怎么在 CI 里工程化。如果你被某个平台的渲染坑过,下篇的踩坑实录里,大概有你的故事。
更多推荐
所有评论(0)