diff options
Diffstat (limited to 'content/notes/d2lang-intro.md')
| -rw-r--r-- | content/notes/d2lang-intro.md | 325 |
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 +``` + |
