+++ date = 2026-03-12 11:22:26 draft = false title = 'D2 入门' +++ [D2](https://d2lang.com/) 是声明式绘图语言,用 Go 编写,配合命令行工具,从纯文本生成 SVG/PNG 格式的图片,支持 UML 类图、时序图、网格图、SQL 数据表、代码、图片和图表等元素。生成出来的图片效果在同类工具中算出众的(对比 [PlantUML](https://plantuml.com/)),由于 PlantUML 生成的图片风格不统一且运行在 Java 虚拟机上(谁家命令行工具用 `.jar` 运行啊……),权衡之后选择了图片类型更少但效果更好的 D2。尽管没有完全实现 UML 而且不支持用例图,但似乎也能用自定义元素实现类似的效果。 ## 安装 D2 官方提供的方法是: ```shell curl -fsSL https://d2lang.com/install.sh | sh -s -- ``` 在 macOS 上可以使用 Homebrew 安装: ```shell brew install d2 ``` ## 命令行 ```shell d2 input.d2 out.svg ``` 加上 `-w` 或 `--watch` Flag,可以监听 `input.d2` 变化,打开浏览器实时更新查看变更。 ```shell d2 -w input.d2 out.svg ``` 输出格式支持 `.png`,修改拓展名即可。 ## 基本语法 D2 主要有三个概念:形状(Shapes)、连接(Connections)和容器(Containers)。 ```d2 x -> y: Hello World ``` 以上语句定义了两个形状之间的连接,并添加了名为「Hello World」的标签(label) ### 定义形状 以下都是合法名称: ```d2 imAShape im_a_shape im a shape i'm a shape # 一个连字符 - 不代表连接,-- 表示一个连接 # 所以 a--shape 代表的是 a 与 shape 的连接 a-shape ``` 用冒号定义形状的标签,也就是显示名称: ```d2 pg: PostgreSQL ``` 可以这样更改形状。 ```d2 pg.shape: cloud ``` 所有形状见[官方文档](https://d2lang.com/tour/shapes/#:~:text=the%20same%20shape.-,SHAPE%20CATALOG,-rectangle)。 对于 `circle` 和 `square` 两个形状,如果手动设置宽高,它们会保持相同的 `1:1` 比例,不会因此变成 `oval` 和 `rectangle`。 ### 定义连接 连接有四种,含义是不言自明的: - `--` - `->` - `<-` - `<->` 连接也可以有标签: ```d2 Shape 1 -- Shape 2: Label ``` 连接可以重复,不会覆盖,而是同时存在: ```d2 Database -> S3: backup Database -> S3 Database -> S3: backup ``` 连接可以多个写在同一行(chaning),标签会同时应用于所有连接: ```d2 High Mem Instance -> EC2 <- High CPU Instance: Hosted By ``` 连接可以定义箭头前后的样式和标签,下图的箭头来源处显示 `1`,目标处显示 `*`(一对多),并且目标处的箭头显示为菱形。 ```d2 a -> b: To err is human, to moo bovine { source-arrowhead: 1 target-arrowhead: * { shape: diamond } } ``` 可以在别处引用某个连接: ```d2 x -> y: hi x -> y: hello (x -> y)[0].style.stroke: red (x -> y)[1].style.stroke: blue ``` ### 定义容器 在一个形状内再定义一个形状,父形状就成为容器。 ``` im a parent.im a child ``` 也可以使用嵌套语法: ``` clouds: { aws: { load_balancer -> api api -> db } gcloud: { auth -> db } gcloud -> aws } ``` ### 关于花括号 `a.b` 的写法和 `a { b: }` 应该是等价的,比如下面两个语句是等价的: ```d2 Shape 1.shape = cloud Shape 1: { shape = cloud } ``` 尽管文档中所有例子都要求有 `:`,但实际测试后发现可以删除,以下语句是合法的: ```d2 Shape 1 { shape = cloud } ``` 除非需要添加标签文本。 ```d2 Shape 1: Label { shape = cloud } ``` ## 特殊对象 ### 文本 这样插入 Markdown 作为形状: ```d2 explanation: |md # I can do headers - lists - lists And other normal markdown stuff | ``` 还可以是 LaTex: ```d2 formula: |latex \lim_{h \rightarrow 0 } \frac{f(x+h)-f(x)}{h} | ``` 代码也可以,标注语言即可,以下是 Go 的例子: ```d2 explanation: |go awsSession := From(c.Request.Context()) client := s3.New(awsSession) ctx, cancelFn := context.WithTimeout(c.Request.Context(), AWS_TIMEOUT) defer cancelFn() | ``` 所以,只要记住插入文本的语法是前后标上 `|`,在第一个 `|` 之后标注语言即可,如果语言是 Markdown 或 LaTex,就会被解析。 如果嵌入的代码中也用到了 `|` 甚至 `||`,就可以把 `|` 替换为 `||` 或 `|||`。 ```d2 my_code: |||ts declare function getSmallPet(): Fish | Bird; const works = (a > 1) || (b < 2) ||| ``` ### 图片和图标 给形状添加 `icon` 属性即可。 ```d2 backup: { icon: https://icons.terrastruct.com/aws%2FStorage%2FAWS-Backup.svg } ``` 如果想显示为独立的图片,将形状设置为 `image` ```d2 backup: { icon: https://icons.terrastruct.com/aws%2FStorage%2FAWS-Backup.svg shape: image } ``` ### SQL 表格 ```d2 objects: { shape: sql_table id: int {constraint: primary_key} disk: int {constraint: foreign_key} json: jsonb {constraint: unique} last_updated: timestamp with time zone } disks: { shape: sql_table id: int {constraint: primary_key} } objects.disk -> disks.id ``` ### UML 类图 ```d2 D2 Parser: { shape: class # Default visibility is + so no need to specify. +reader: io.RuneReader readerPos: d2ast.Position # Private field. -lookahead: "[]rune" # Protected field. # We have to escape the # to prevent the line from being parsed as a comment. \#lookaheadPos: d2ast.Position +peek(): (r rune, eof bool) rewind() commit() \#peekn(n int): (s string, eof bool) } ``` ### UML 时序图 ```d2 Office chatter: { shape: sequence_diagram alice: Alice bob: Bobby awkward small talk: { alice -> bob: uhm, hi bob -> alice: oh, hello icebreaker attempt: { alice -> bob: what did you have for lunch? } unfortunate outcome: { bob -> alice: that's personal } } } ``` ### 网格图 见[文档](https://d2lang.com/tour/grid-diagrams/)。 ### UML 用例图 D2 不直接支持用例图,但有一个形状是 `person`,可以用来代表用户,应该可以这样实现用例图: ```d2 user.shape = person user -> Use Case 1 user -> Use Case 2 user -> Use Case 3 Use Case 4 <- Use Case 3: extends ```