Mermaid 时序图编辑器
时序图按事情发生的先后展示参与方之间的消息,时间自上而下。当问题是谁按什么顺序调用了谁、又拿回了什么时用它——接口握手、认证流程、重试逻辑。如果重点是某一个组件内部的分支逻辑,流程图更合适。
OAuth 2.0 授权码流程
时序图的典型用武之地:四个参与方、一次用户看不见的跳转,以及一次必须在服务端之间完成的令牌交换。这种事用文字讲很吃力,用流程图则根本表达不出顺序。
sequenceDiagram
autonumber
participant U as 用户
participant B as 浏览器
participant A as 认证服务器
participant API as 资源接口
U->>B: 点击"登录"
B->>A: GET /authorize?client_id&redirect_uri
A-->>B: 跳转到登录页
U->>A: 提交账号密码
A-->>B: 302 跳回 redirect_uri 并带上 code
B->>API: 用 code 请求 POST /token
API->>A: 交换 code (服务端之间)
A-->>API: access_token 与 refresh_token
API-->>B: 下发会话 Cookie
B-->>U: 登录完成实例讲解
1. 两个参与方,一来一回
`->>` 是实线实心箭头,习惯上表示请求。`-->>` 是虚线,习惯上表示响应。这只是约定、并不强制,但破坏它会让图很难扫读。
sequenceDiagram
客户端->>服务端: GET /orders
服务端-->>客户端: 200 返回订单列表2. 别名与激活条
`participant X as 长名称` 能让箭头保持简短、方框保持可读。`activate` 和 `deactivate` 画出参与方处于忙碌状态的时间条——用来暴露一次很慢的下游调用非常合适。
sequenceDiagram
participant API as 订单接口
participant DB as Postgres
API->>DB: SELECT * FROM orders
activate DB
DB-->>API: 返回 4200 行
deactivate DB
API->>API: 序列化响应3. 用 alt 和 opt 表达分支
`alt`/`else` 是二选一,`opt` 是可能不发生的步骤。这些块每一个都必须用 `end` 收尾——块没闭合是时序图最常见的错误,而且 Mermaid 会把它报在文件末尾而不是块本身。
sequenceDiagram
participant C as 收银台
participant P as 支付服务商
C->>P: 授权 49.90 元
alt 授权成功
P-->>C: 返回授权码
C->>C: 标记订单已支付
else 授权失败
P-->>C: 返回失败原因
C->>C: 释放库存占用
end
opt 风控评分偏高
C->>C: 转入人工复核队列
end4. 重试循环与备注
`loop` 把重复的消息括起来,而 `Note over` 正适合写下读者否则一定会问的东西——超时时间、上限次数、为什么要限制重试。
sequenceDiagram
participant W as 工作进程
participant S as 搜索索引
Note over W,S: 最多重试 5 次,之后进入死信队列
loop 最多 5 次
W->>S: PUT /documents/42
S-->>W: 503 服务不可用
W->>W: 指数退避等待
end
W->>W: 移入死信队列5. 并行处理与自调用
`par` 展示同时进行的工作——这恰恰是文字最难表达的。参与方指向自己的箭头,是在不虚构组件的前提下表现内部处理的正当写法。
sequenceDiagram
participant O as 订单服务
participant M as 邮件服务
participant I as 发票服务
participant A as 数据统计
O->>O: 提交事务
par 通知客户
O->>M: 发送确认邮件
and 生成单据
O->>I: 生成发票 PDF
and 记录指标
O->>A: 上报 order_created 事件
end
O-->>O: 向调用方返回 201时序图语法速查
时序图有自己的一套箭头词汇。这些在流程图里都不成立,而流程图的箭头在这里多半也不是你以为的意思。
| 语法 | 含义 |
|---|---|
| sequenceDiagram | 开启该图。区分大小写——`sequencediagram` 会失败。 |
| participant A | 声明参与方,同时固定从左到右的顺序。 |
| participant A as 名称 | 别名——箭头里用短 ID,方框里显示全名。 |
| actor A | 与 participant 相同,但画成小人图标。 |
| A->>B: 文字 | 实线实心箭头。习惯上表示请求。 |
| A-->>B: 文字 | 虚线实心箭头。习惯上表示响应。 |
| A->B: 文字 | 实线,无箭头。 |
| A-)B: 文字 | 开口箭头——习惯上表示异步消息。 |
| A-xB: 文字 | 末端带叉的箭头——习惯上表示丢失或失败的消息。 |
| activate A / deactivate A | 画出表示 A 正忙的激活条。 |
| alt 条件 / else 条件 / end | 互斥分支。 |
| opt 条件 / end | 可能不执行的块。 |
| loop 文字 / end | 重复的消息。 |
| par 文字 / and 文字 / end | 并发分支。 |
| Note over A,B: 文字 | 跨参与方的备注。也可用 `Note left of`、`Note right of`。 |
| autonumber | 自动给每条消息编号。 |
会让时序图报错的六个问题
均以 Mermaid 11.12.2 复现。把出错版本粘进编辑器即可看到确切报错,修正版本可正常渲染。
你会看到
报错指向图的最后一行
原因
有块被打开却从未闭合。`alt`、`opt`、`loop`、`par` 都需要配对的 `end`。Mermaid 直到输入耗尽才发现问题,于是把责任推给最后一行,而不是你漏掉的那个块。
解决办法
数一数开块的数量和 `end` 的数量。报错行如果是文件最后一行,几乎必然是这个原因。
sequenceDiagram
A->>B: 请求
alt 成功
B-->>A: 确认sequenceDiagram
A->>B: 请求
alt 成功
B-->>A: 确认
end你会看到
在消息行上报 Parse error
原因
消息没有冒号。每个箭头后面都需要 `: 文字`,哪怕内容看上去不言自明。
解决办法
补上冒号和标签。
sequenceDiagram
客户端->>服务端 下单sequenceDiagram
客户端->>服务端: 下单你会看到
No diagram type detected matching given configuration
原因
关键字大小写写错了。Mermaid 的图类型关键字区分大小写,`sequencediagram` 与 `sequenceDiagram` 不是同一个标记。
解决办法
把 D 大写。
sequencediagram
A->>B: 你好sequenceDiagram
A->>B: 你好你会看到
Trying to inactivate an inactive participant (B)
原因
有 `deactivate` 却没有配对的 `activate`。与上面那些语法错误不同,这是一次语义检查,所以报错是一句可读的话而不是一堆标记——但图同样画不出来。
解决办法
让每个 `deactivate` 都有对应的 `activate`,或者干脆两个都去掉,让箭头自己说话。
sequenceDiagram
A->>B: 请求
deactivate BsequenceDiagram
A->>B: 请求
activate B
B-->>A: 响应
deactivate B你会看到
在参与方名称之后报 Parse error
原因
participant 声明里写了冒号。这条声明只接受一个名称或一个 `as` 别名,别的都不行。
解决办法
用 `as` 指定显示名称。
sequenceDiagram
participant API: 订单服务
API->>DB: 查询sequenceDiagram
participant API as 订单服务
API->>DB: 查询你会看到
`end` 变成了一个参与方,而不是结束块
原因
`end` 在这里同样是关键字。决定块在哪里结束的是标记而不是缩进,所以叫 `end` 的参与方会和块结束符撞车。
解决办法
永远不要把参与方命名为 `end`。改大小写或换个名字。
sequenceDiagram
A->>end: 收尾sequenceDiagram
A->>Endpoint: 收尾渲染须知
以下均为针对本站所用 Mermaid 11.12.2 的实测结果。时序图在两个方面与这里的其他图类型都不一样。
参与方名称用中文不会让图变宽
这是中文用户最容易担心、实际却不必担心的一点。实测把 `participant A as Order service` / `participant B as Payment provider` 换成 `订单服务` 和 `支付服务商`,渲染出的 viewBox 完全一致,都是 450×215。原因见下一条:时序图的宽度由参与方的数量和间距决定,而不是由标签长度决定——除非名称长到撑破默认间距,否则中文名称是免费的。
宽度取决于参与方数量,而不是消息数量
两个参与方之间的 3 条消息,viewBox 约为 450×309;同样两个参与方之间的 40 条消息是 450×2011——宽度纹丝不动。增加参与方会让图变宽,增加消息只会让它变长。实际结论是:超过大约六个参与方,时序图在笔记本屏幕上就宽到读不下去了,而这远在消息数量成为问题之前。
时序图的 viewBox 原点是负数
本站其他所有图类型的 viewBox 都从 `0 0` 开始,时序图从 `-50 -10` 开始——Mermaid 为参与方方框在左侧和上方预留了空间。如果你要对导出的 SVG 做后续处理,这一点很重要:默认原点为零的裁剪代码会把最左边的参与方切掉。
PNG 导出在这里是原生可用的
时序图的标签是普通 SVG 文本,而不是内嵌 HTML——这一点和流程图、类图、状态图、ER 图都不同。浏览器可以直接把它光栅化,所以时序图导出的 PNG 与屏幕上逐像素一致:不需要重新渲染,也不会有字体排版上的偏移。
每条消息大约占 45 像素高
用来在动笔之前估算一张图能不能塞进一页幻灯片。二十条消息大约 900 像素高,这差不多就是截图还能不滚动就看清的上限。
什么时候该换一种图
如果大部分消息都是某个参与方发给自己的,那你描述的是一个算法而不是一次对话,用流程图会更好读。
如果你开始在 `alt` 块里再套 `alt` 块,说明分支已经超出了这种图能承载的范围。时序图能把系统中的一条路径画得非常漂亮,把所有路径画得非常糟糕。在这里画正常路径,把异常处理放到另一张图里。
而如果你真正要传达的是系统里有哪些组件、它们如何连接——而不是它们按什么顺序通信——那么时序图帮不上忙。那是架构图,用 Mermaid 的流程图配合子图更合适。
其他图类型
作者 Dominik Malsch · 最后更新: