D2 图表
D2 是一种声明式语言:你描述形状及其之间的连接,布局引擎决定所有元素的位置。你永远不需要手动放置坐标。这也是为什么 D2 图表简短、可差异化且易于后续编辑,同时也意味着大多数错误源自语法和布局引擎的怪癖,而非设计问题。
本技能提供了经实际编译器验证的完整 D2 v0.9.x 工作知识。references/ 和 assets/templates/ 中的每个示例均通过编译。
工作流程
- 检查工具。 运行
d2 --version(或scripts/d2check.sh --version)。如果d2缺失,告知用户并提供brew install d2或curl -fsSL https://d2lang.com/install.sh | sh -s --的安装命令;不要静默安装。 - 选择图表类型 并查阅对应的参考文档(见下表)。当有适合的模板时,从
assets/templates/中的模板开始。 - 编写
.d2文件 到用户期望的位置(默认路径:diagrams/<name>.d2)。 - 使用
scripts/d2check.sh <file.d2> [outdir] [pdf|pptx|gif]进行编译和渲染(格式化、编译、渲染 SVG,将 PNG 预览放入outdir/preview/,以及任何额外格式)。永远不要单独依赖d2 validate:它仅解析语法。错误的形状名称、超出范围的样式、不支持的布局功能和缺失的导入都会通过validate验证,但在编译时失败。 - 查看图片。 打开
preview/中的 PNG(使用 Read 工具查看.png),并判断其效果:重叠的标签、混乱的边、错误的流向、不平衡的布局。修复后重新渲染。一个能编译但不可读的图表不算完成。 - 交付
.d2源文件以及渲染后的文件,并说明你所用的布局引擎、主题和方向,以便用户进行更改。
对于快速的一次性使用(无需创建文件):echo 'a -> b' | d2 - out.svg。
一屏语法速览
direction: right # up | down | right | left(根级别;仅 TALA 支持按容器设置)
# 形状:键值、可选标签、可选属性
web: Web App {shape: rectangle} # key `web`, label "Web App"
db: Postgres {shape: cylinder}
user: Customer {shape: person}
# 连接:-> <- <-> -- (始终引用键,而不是标签)
user -> web: uses
web -> db: SQL {style.stroke-dash: 3}
web -> cache: reads # 未声明的键会被即时创建
# 容器:点号或嵌套映射
backend: {
api -> worker: enqueue
worker -> _.db: writes # _ = 父作用域
}
web -> backend.api: HTTPS
# 样式位于属性中
db.style.fill: "#e6f2ff" # 以 # 开头的内容需要加引号
(web -> db)[0].style.stroke: red # 通过索引为连接设置样式
# 重用:类、变量、通配符
classes: {
store: {shape: cylinder; style.fill: "#e6f2ff"}
}
cache.class: store
vars: {env: prod}
note: Running in ${env}
*.style.border-radius: 8
分号用于分隔同一行中的多个项。# 表示注释开始,""" ... """ 为块注释。键名不区分大小写,重复的声明会合并(API、api 和 Api: Gateway 是同一个标签为 "Gateway" 的形状)。
引号问题:错误的头号来源
未加引号的字符串虽然方便,但许多字符具有特殊含义。当标签包含以下任何字符时,务必对整个标签加引号("..." 或 '...'),否则编译器会报错,甚至更糟糕地静默截断标签:
| 字符 | 未加引号时发生的情况 | 修复示例 |
|---|---|---|
# 出现在任意位置 | 该行其余部分变为注释:a: C# language 渲染为 "C" | a: "C# language" |
[ 在开头 | 被解析为数组:a: [JSON/HTTPS] 仅渲染 "a" | a: "[JSON/HTTPS]" |
. 在键中 | 创建容器:svc v1.2 变为 svc v1 > 2 | "svc v1.2" |
& 或 [ ] 在键中 | 解析错误(& 是通配符过滤器标记) | "R&D", "svc[0]" |
$ 后跟非 { 的内容 | "substitutions must begin on {" | 单引号:'costs $5' |
{ } ` | ` 在标签开头 | 打开映射或块字符串 |
保留字作为形状键(label, link, class, top, style, shape, icon, near, direction, vars ...) | "reserved keywords are prohibited in edges" | "link" -> b,或选择其他键 |
-> 或 : 在边标签内部 | 实践中没问题(a -> b: Time: 10s 工作正常) | 不需要加引号 |
非 ASCII 标点,如 :(全角冒号) | 不被识别为冒号 | 使用 ASCII : ; . |
十六进制颜色必须加引号("#fff"),因为 # 会启动注释。单引号还会禁用 ${...} 替换。
标签中的换行:在带引号或不带引号的标签中使用 \n,或使用块字符串 |md ... | 实现富文本。
容易踩坑的地方(调试前请阅读)
- Dagre(默认布局引擎)无法在容器与其自身后代之间绘制连接(
a -> a.b),无法设置容器大小(width/height),也无法使用near: <object>。它会在编译时报错。请切换到--layout=elk(或vars.d2-config.layout-engine: elk),而不要重写整个图表。 - 保留字匹配时不区分大小写。 名为
Shape、Label、Class、Style、Link、Icon的形状或 UML 类会与关键字冲突(reserved field shape does not accept composite)。请重命名。 - 连接键,而不是标签。 在
be: Backend之后,Backend -> x会创建一个名为 Backend 的新形状。请使用be -> x。 - 重复的连接不会合并;两次写
a -> b会绘制两条箭头。使用(a -> b)[0]、(a -> b)[1]来分别引用它们。 d2-legend是一个变量,不是形状:将其放在vars: { d2-legend: { ... } }内部。顶级的d2-legend: ""只是一个名为 "d2-legend" 的普通形状。- 时序图:声明的顺序即为屏幕上的顺序,参与者应首先声明,在组内只能引用顶层存在的参与者。
- SQL 表和 UML 类:连接行(
orders.user_id -> users.id),并使用 ELK 或 TALA 以便边能精确落在对应的行上。 - 箭头形状仅在有箭头的端点上渲染:
A -> B配合source-arrowhead.shape: diamond在 A 端不会绘制任何形状(而--不会绘制任何形状)。请翻转边,使形状位于目标端,或使用<->。 d2 fmt会重写保留字标签(lnk: Link变为lnk: link),而scripts/d2check.sh会在原地运行fmt操作。请给这类标签加引号(lnk: "Link")。- 块字符串
|md ... |在下一个|处结束。内部的字面管道符需要使用更长的分隔符:|md ...|或||md ... ||。 - 数值样式范围被强制检查:
stroke-width0-15,opacity0-1,font-size8-100。text-transform接受none|uppercase|lowercase|capitalize(官网说title;编译器拒绝)。3d仅适用于矩形、正方形和六边形。 - 除非被要求,否则不要添加
animated: true或dark-theme-id。 动画暗示运行时数据流;暗色主题会静默地为使用系统暗色模式的观看者翻转颜色。 - 形状上的填充颜色会覆盖主题。 优先使用主题和少量
classes;只有当颜色携带含义时才使用显式颜色(并添加图例)。 - v0.9+ 版本无需浏览器即可渲染 PNG/PDF/PPTX/GIF。 较旧版本(<0.8)需要 Playwright/Chromium,首次使用时会自动下载。请检查
d2 --version。 - TALA 打包在 v0.9 中(
--layout=tala),但非确定性:一个微小的标签变化可能会重排整个布局。如果要求可重复性,请使用data.tala-seeds固定种子。 - 多板文件(
layers/scenarios/steps)导出 SVG 和 PNG 时为目录形式(x/index.svg、x/layers/a.svg),将 PNG 渲染到相同名称会替换 SVG 目录。请将不同格式保存在不同文件夹中(脚本已实现)。对于逐层深入的交付物,使用 PDF(每板一页,可点击的link: layers.x),并且不要手动绘制 "返回" 形状:导出器会自动添加面包屑栏。详情请见references/composition.md。 - 输出文件存在不代表成功。 D2 可能在出错时写入部分渲染结果。请检查退出码(脚本已实现)。
选择布局、方向、主题
- 布局引擎:
dagre(默认,快速,弯曲边)->elk(正交布线,最适合架构、SQL 表、容器到容器连接)->tala(对称、按容器设置direction、near: object、锁定top/left)。对于包含容器或表的场景,默认为 ELK。 - 方向:管道、请求流和宽屏使用
right;层级结构和组织架构图使用down。为整个面板选择一个方向。 - 主题:
theme-id: 0默认;4Cool classics 和3Flagship Terrastruct 外观现代;1中性灰适合正式文档;8色盲友好;300/301终端;303C4;200/201暗色。完整表格见references/cli-and-rendering.md。 - 将渲染设置放在文件中,以便随文件一起携带:
vars: {
d2-config: {
layout-engine: elk
theme-id: 4
pad: 40
}
}
(CLI 标志和 D2_* 环境变量会覆盖此块。)
设计指导
- 每板 3 到 12 个形状。 超出此范围,使用
layers(放大查看)、scenarios(变体)或steps(逐步构建)拆分,或使用容器进行分组。 - 用键命名,用标签给人看:短的稳定键(
pg、auth),可读的标签(pg: PostgreSQL)。 - 边标签是动词或协议(
reads、publishes、HTTPS),不是句子,也不是目标名称。 - 形状匹配含义:
cylinder存储、queue队列、person人员、cloud外部系统、diamond决策、hexagon服务、document/page文档、package包、step步骤。不要让所有东西都是矩形。 - 颜色编码含义或不做颜色编码。 最多约 5 个类别,通过
classes应用,仅当颜色承载了标签未包含的信息时才使用d2-legend。 - 每板保持一个抽象层次。
- D2 用于在白板规模(最多几百个形状)上记录软件和系统。它特意不包含甘特图、思维导图、韦恩图、桑基图、饼图/柱状图或地理地图,并且一个对象不能位于两个容器中。如果需要,请向用户说明并建议使用其他工具,而不是强行使用 D2。
参考文档索引
仅阅读任务所需的部分。
| 任务 | 阅读文档 |
|---|---|
| 任何语法疑问:键、标签、连接、容器、字符串、转义、注释、保留关键字 | references/language.md |
形状、所有样式键及其范围、箭头、文本/Markdown/LaTeX/代码、图标、near 定位、尺寸、主题 | references/shapes-and-styling.md |
| 时序图、SQL 表(ERD)、UML 类、网格、图例、工具提示/链接、C4 | references/diagram-types.md |
类、变量、通配符和过滤器、覆盖和 null、导入、suspend 模型 | references/reuse-and-globs.md |
| 层、场景、步骤、板间链接、大型图表导入、多板导出 | references/composition.md |
CLI 标志、布局引擎、主题表、导出格式、d2-config、嵌入到文档/CI、D2.js | references/cli-and-rendering.md |
| 每种图表家族的现成模式(架构、流程图、状态机、ERD、C4、组织架构图...) | references/recipes.md + assets/templates/*.d2 |
| 从 Mermaid / PlantUML / Graphviz 转换 | references/recipes.md(转换部分) |
| 错误消息或渲染错误的图表 | references/troubleshooting.md |
编译失败时的处理
运行 scripts/d2check.sh file.d2。错误格式为 file:LINE:COL: message。在 references/troubleshooting.md 中查找该消息;常见元凶包括引号问题(上文)、布局引擎限制(切换到 ELK)、未知形状或超出范围的样式,以及块字符串的缩进问题。每次修复一个错误并重新运行:后面的错误往往是第一个错误导致的连锁反应。