summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoreltrac <hi@eltr.ac>2026-03-12 11:22:42 +0800
committereltrac <hi@eltr.ac>2026-03-12 11:22:42 +0800
commitf1fab8f321e60add4ef53f6cdceac51653e36af8 (patch)
tree35da408f54dd604691b445dfa6135df2e217deb0
parent0f66efa7ae2b1681088f5d51519539eb5e326476 (diff)
docs: new note
-rw-r--r--content/notes/d2lang-intro.md325
1 files changed, 325 insertions, 0 deletions
diff --git a/content/notes/d2lang-intro.md b/content/notes/d2lang-intro.md
new file mode 100644
index 0000000..900a190
--- /dev/null
+++ b/content/notes/d2lang-intro.md
@@ -0,0 +1,325 @@
++++
+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
+```
+