Mermaid 泳道图(Swimlanes Diagram)语法详解:从 swimlane-beta 语法到源码级实现原理
【免费下载链接】mermaidGeneration of diagrams like flowcharts or sequence diagrams from text in a similar manner as markdown项目地址: https://gitcode.com/GitHub_Trending/me/mermaid
本文基于 Mermaid 仓库中的官方语法文档 docs/syntax/swimlanes.md 展开,系统讲解泳道图(Swimlanes Diagram)的完整语法——方向声明、泳道定义、节点形状、连线语法与无障碍标注,并结合 swimlanesDiagram.ts 等源码,解释"布局变体图"如何复用 flowchart 解析器与渲染器、泳道布局引擎(Sugiyama 管线)的工作方式,以及默认泳道、主题变量、样式语句在实现中的实际行为。读完本文,你可以直接编写生产可用的swimlane-beta图,并理解其底层的布局与渲染机制。
需要说明的适用前提:泳道图是 Mermaid 的新图表类型,自v11.16.0+引入,官方文档明确提示其语法可能在后续版本中演进,使用时以当前仓库文档为准。
什么是泳道图,适合什么场景
泳道图将流程按"职责"切分:每条泳道(lane)代表一个角色、团队、系统或阶段,泳道内的节点表示该职责范围内发生的工作,箭头表示工作顺序以及跨泳道的交接(handoff)。
官方文档给出的核心判断标准是:当你关心的不只是"下一步发生什么",还有"谁负责这一步"时,就适合用泳道图。典型应用包括审批流程、支持工单处理、交付工作流,以及任何工作会跨越团队或系统的流程。
与相邻图型的边界(引自原文档 "When to Use Another Diagram" 一节):
- 所有权不重要、只需展示顺序或分支 → 用普通 flowchart;
- 关注点是在时间轴上参与者之间的消息 → 用 sequenceDiagram;
- 关注点是单个事物如何改变状态 → 用 stateDiagram。
基本示例
官方文档中的第一个完整示例是一个"客户 / 客服 / 工程"三方支持流程。注意文档提示:在线示例渲染使用 Neo 外观与 Redux 主题,而开箱即用(out of the box)的泳道图遵循你配置的默认 look 和 theme:
这条流程里有两处跨泳道交接:客服分诊后按"已知问题/需要改代码"分叉,工程侧修复完成后回到客服的answer节点,最终回流到客户的receive节点——这正是泳道图相比普通流程图多表达出来的"责任变化点"。
语法:swimlane-beta关键词与方向声明
泳道图以swimlane-beta关键词开头,其后可选跟一个方向:
swimlane-betaswimlane-beta LR支持的方向及其含义(完整继承自原文档的方向表):
| Direction | Meaning |
|---|---|
TB | Top to bottom |
TD | Top down, same asTB |
BT | Bottom to top |
LR | Left to right |
RL | Right to left |
不写方向时默认使用TB。
源码印证一:解析器层面。在 flow.jison 中,swimlane-beta被注册为与graph/flowchart同级的图类型关键词:
"swimlane-beta" {if(yy.lex.firstGraph()){this.begin("dir");} return 'GRAPH';}也就是说swimlane-beta走的正是 flowchart 的词法入口(firstGraph+dir状态),这从机制上解释了为什么它的方向词、节点形状、连线、classDef、style、linkStyle都能直接复用 flowchart 语法。测试 flow.spec.js 也断言了swimlane-beta LR;A-->B;可以被解析。
源码印证二:图类型检测。插件定义在 detector.ts 中,检测规则是对文本做正则匹配:
const detector: DiagramDetector = (txt) => { return /^\s*swimlane-beta\b/.test(txt); };即文本必须以swimlane-beta开头(前导空白允许),命中后异步加载swimlanesDiagram.ts。这也意味着swimlane-beta必须出现在文档首行。
源码印证三:默认方向。e2e 测试 swimlanes.spec.ts 中名为defaults to the swimlanes layout without an explicit layout config的用例验证了:不写layout配置时,图自动使用泳道布局,且每条顶层subgraph都渲染为g.cluster.swimlane元素(测试断言了 2 条泳道对应 2 个 cluster)。
泳道(Lanes):顶层 subgraph 即泳道
在泳道图中,顶层 subgraph 被渲染为泳道,泳道以end结束。最小示例:
泳道可以带内部 id 和显示标签,这在标签含空格、或需要稳定 id 用于后续样式引用时很有用:
默认泳道行为(源码补充,原文档未覆盖)。swimlanes.spec.ts 中的puts nodes without an explicit subgraph into a default swimlane用例表明:如果节点没有被任何subgraph包裹,它会被自动归入一个 id 为__swimlane_default__的默认泳道(测试断言了g.cluster.swimlane[data-id="__swimlane_default__"]存在)。这解释了为什么在泳道图里"裸节点"不会渲染失败,而是落入一条隐式泳道。
节点:复用 flowchart 形状语法
节点使用 flowchart 风格的形状语法:id 写在前面,标签写在形状内部:
最常见的节点形式(完整继承自原文档的节点表):
| Syntax | Shape | Common use |
|---|---|---|
id[Text] | Rectangle | Task or activity |
id(Text) | Rounded rectangle | Step or event |
id([Text]) | Stadium | Start or end |
id{Text} | Decision | Branching question |
id((Text)) | Circle | Connector or marker |
完整的形状目录(含图标、图片、markdown 字符串、class 与样式选项)参见 flowchart 语法文档。
样式能力同样完整继承(源码补充)。swimlanes.spec.ts 中的多个用例证实了以下能力在泳道图中全部可用:
style A fill:#ff99cc,stroke:#003366,stroke-width:5px,color:#111111—— 节点样式语句;linkStyle 0 stroke:#ff6600,stroke-width:5px—— 按序号修饰连线;classDef highlighted fill:#bbf,...+class A highlighted—— 类定义与类应用,且节点会带上highlightedclass;themeVariables(如mainBkg、nodeBorder、lineColor)—— 主题变量同样生效。
此外 handdrawn(rough)外观也被覆盖:测试以look: 'handDrawn'渲染了菱形决策、带标签连线和多泳道 TB 图,节点以g.rough-node呈现。
连线:同泳道与跨泳道
连线同样使用 flowchart 风格语法,可连接同一泳道内的节点,也可跨泳道:
常见连线形式(完整继承自原文档的连线表):
| Syntax | Meaning |
|---|---|
A --> B | Arrow |
A --- B | Line without arrowhead |
A -->|Label| B | Arrow with label |
A -.-> B | Dotted arrow |
A ==> B | Thick arrow |
完整连线语法(含双向箭头、最小连线长度)参见 flowchart 语法的 "Links between nodes" 一节。
跨泳道连线的布局处理(源码补充)。泳道图最大的布局难点是跨泳道边。从 pipeline.ts 的sugiyamaLayout可以看到布局管线是经典 Sugiyama 四阶段结构,且跨泳道边是显式的一等参数:
const ignoreCrossLaneEdges = opts?.ignoreCrossLaneEdges ?? true; // Phase 1: cycle removal const cycleRes = removeCycles_DFS(g0); // Phase 2: layering const layering = ignoreCrossLaneEdges ? assignLayers_LaneAwareCompact(gAcyclic, {...}) : assignLayers_Gravity(gAcyclic, {...}); // Phase 3: ordering(含 laneOrder 优化) const ordered = orderLayers(properLayering, graphWithDummies, { laneOrder }); // Phase 4: coordinates const coordinates = assignCoordinates(ordered, graphWithDummies, {...});可以推断的默认行为:分层(layering)阶段默认忽略跨泳道边(ignoreCrossLaneEdges默认true),即跨泳道交接不会破坏各泳道内部的层次紧凑性,而是交由 Phase 4 的坐标分配与正交路由器(orthogonalRouter/)处理走线;另有automaticLaneOrdering选项可自动优化泳道排列顺序以减少交叉。该目录下的.ddlt.spec.ts用例(如15-border-hugging-lr、7-car-sales-constr)与 validateLayout.ts 中的校验逻辑(如"节点贴边/触碰泳道边框"检测)表明布局结果有专门的回归校验保障。
无障碍标注:accTitle 与 accDescr
使用accTitle和accDescr提供可访问标题与描述:
渲染后的 SVG 会携带aria-roledescription="swimlane"(见 swimlanes.spec.ts 中assertStandaloneSwimlanesRendered的断言),屏幕阅读器可据此识别图表类型,再结合accTitle/accDescr提供语义。
布局引擎原理:泳道图是"布局变体图"
理解泳道图与 flowchart 的关系,是理解它"为什么语法几乎就是 flowchart"的关键。swimlanesDiagram.ts 的头部注释写得很直白:
// Swimlanes is a "layout-variant diagram": it reuses the flowchart parser, DB, // and renderer wholesale and only swaps in a different layout engine // (`defaultLayout: 'swimlane'`) plus lane-specific styles. export const diagram = createFlowDiagram({ defaultLayout: 'swimlane', styles: swimlanesStyles });即:泳道图完整复用 flowchart 的解析器、数据模型和渲染器,只替换布局引擎(defaultLayout: 'swimlane')并追加泳道专属样式。这是该项目对"跨图型隔离"规则唯一被允许的例外,且只依赖 flowchart 的公开入口(createFlowDiagram),不触碰其内部。
两个直接推论,均有测试佐证:
- 默认布局优先级:flowDiagram.spec.ts 断言
defaultLayout(如swimlane)优先于站点配置的layout值,除非用户显式覆盖——所以swimlane-beta图不需要任何额外配置就走泳道布局。 - 样式叠加:styles.ts 在 flowchart 全部样式之上追加两条规则:
.swimlane.cluster rect用主题色clusterBorder画泳道边框(主题自适应,而非硬编码颜色),Neo 外观下关闭 cluster 的模糊滤镜。
泳道布局的测试夹具(swimlane-beta独立声明)保存在 e2e/platform/dev-diagrams/layout-tests/swimlanes 目录,包含1-simple、4-car-fun-sales-tb、7-car-sales-constr、8-query-process-2等场景,e2e 套件会扫描该目录自动纳入快照回归,可用于对照真实布局效果。
最佳实践(完整继承原文档 Good Practices)
以下五条实践均直接来自原文档,各附其示例。
1. 每条泳道只表达一种"所有权"
泳道的选择应回答"谁负责这一步?",除非区分本身正是图的重点,否则不要把团队、阶段、状态混在同一条泳道里。
2. 为跨泳道交接打标签
跨泳道箭头就是责任变化点。当交接依赖某份文档、决策、消息或条件时,给箭头加标签。
3. 保持长流程可读
当泳道或交接点放不进一屏时,把大流程拆成多张图。一张好用的泳道图通常不需要"把每条箭头追踪两遍"就能读懂。
4. 使用稳定的 id
节点和泳道都用短而有含义的 id。这样标签可以随意改,而不会破坏连线、样式或后续引用(配合classDef/class尤为明显):
5. 把决策放在做出决策的泳道里
决策节点应放在拥有该决策权的泳道中,再把结果路由到执行后续动作的泳道:
总结:泳道图的实现全景
| 维度 | 语法/行为 | 仓库证据 |
|---|---|---|
| 图类型识别 | 首行swimlane-beta,正则/^\s*swimlane-beta\b/检测 | detector.ts |
| 解析 | 复用 flowchart 词法(GRAPH关键词)与解析器 | flow.jison |
| 架构 | "布局变体图":复用 flowchart parser/DB/renderer,仅换布局引擎 | swimlanesDiagram.ts |
| 方向 | TB/TD/BT/LR/RL,默认TB | docs/syntax/swimlanes.md |
| 泳道 | 顶层subgraph渲染为g.cluster.swimlane;裸节点落入__swimlane_default__默认泳道 | swimlanes.spec.ts |
| 布局 | Sugiyama 四阶段管线,跨泳道边默认不参与分层 | pipeline.ts |
| 样式 | 完整继承style/classDef/linkStyle/主题变量;泳道边框主题自适应 | styles.ts |
一句话概括:泳道图在语法上就是"带方向声明的 flowchart + 顶层 subgraph 升格为泳道",在实现上是 Mermaid 用"布局变体"模式(只换布局引擎、复用解析与渲染)交付的一个新图型,因此它能天然获得 flowchart 的全部节点形状、连线、样式与主题能力,同时由专用的泳道布局管线保证跨泳道交接的排布质量。
【免费下载链接】mermaidGeneration of diagrams like flowcharts or sequence diagrams from text in a similar manner as markdown项目地址: https://gitcode.com/GitHub_Trending/me/mermaid
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考