“diagram-design”这个项目标题乍一看很简洁,但我第一反应是:这不就是把“画图”这件事重新定义了一遍吗?接触过架构图、流程图、思维导图的人应该都有同感——我们真正需要的不是一张静态的图,而是一套能改、能查、能复用、能多人协作的图表设计体系。diagram-design本质上就是把这个诉求产品化:通过代码或结构化文本来描述图表结构,再交给渲染引擎生成可视化结果。它能解决的问题很明确:版本混乱、协作低效、图表与代码不同步、大图改起来费劲。这篇文章适合正在为团队搭建技术文档体系的人,也适合做后端、前端、DevOps,甚至产品经理和架构师——只要你的工作里绕不开图表,这套思路就能帮你省掉大量重复劳动。
1. 从“画图”到“设计”:diagram-design的思路拆解
1.1 传统绘图方式为什么撑不住了
很多团队一开始用的是Visio、ProcessOn、draw.io这类拖拽式工具。刚接触时确实方便,拉一个框、连一根线,十分钟就能出一张像样的流程草图。但用久了问题就出来了——最典型的是版本管理彻底失控。我见过一个真实案例:架构组把系统部署图存在共享网盘里,第二周发现有三个不同时间的备份,谁也说不好哪张才是最新。更麻烦的是,图里的某些模块已经下线了,但画图的人离职了,后面接手的人只能对着旧图猜。
拖拽式工具还有个隐蔽的坑:一旦图变得复杂,微调就是灾难。框和线是绝对定位的,挪动一个节点,旁边跟它关联的十几根线全要手动画一遍;为了躲开文字重叠,又要逐个调整坐标。这个过程的重复劳动率极高,而且每一轮改动都可能引入新的错位。Slack上对着一张图来回截屏沟通,往往比重新画一张还慢。
再往后,当代码仓库里开始用Markdown写文档时,大家发现了一个更大的别扭:图和文是割裂的。文档在Git里,图在网盘里;代码更新了,架构图没人更新;审PR的时候,必须单独下载附件才能看。这就像写代码的时候,把函数实现放在另一个神奇的外部文件里,调用了却没有任何引用检查——早晚要出事故。
1.2 diagram as code的核心思路:把图表当代码管理
diagram-design走的路线,简单说就是“diagram as code”——图表即代码。不再用鼠标拖拽,而是用文本语言描述图表的结构和样式,然后通过解析器生成SVG、PNG或交互式HTML。图表从那一刻开始,就有了和普通代码一样的生命周期:可以提交到Git,可以diff,可以code review,可以自动渲染,可以嵌入文档系统,可以参与CI流程做一致性校验。
这个转变的底层逻辑,和当年从“手写HTML”到“组件化开发”是一样的——把重复性强、容易出错的部分交给工具,把表达和设计留给人类。以前画图的核心动作是“调整坐标”,现在核心动作变成“描述关系”。我只需要说“服务A调用了服务B,服务B依赖数据库C”,渲染层会自动完成布局、对齐、连线的计算。这个抽象层级的变化,才是diagram-design真正值钱的地方。
对我个人来说,最上头的点是:所有图表都可以review了。以前架构评审会,几个人围着投影仪看一张静态图;现在直接在评论里@人,指出“这个模块依赖方向画反了”,改一行文本,渲染结果立即更新。图表和代码长在同一个仓库里,散落在同一个PR里,谁改了模块调用关系,一并更新架构图,谁也不会忘。
这个思路适配的场景非常广,而且不限行业。研发团队画系统架构、数据流、网络拓扑可以用;业务团队画用户旅程、业务流程图也可以用;做知识管理的人画概念图、思维导图同样顺滑。你只需要掌握一套足够表达关系的文本语法,剩下的事交给引擎去排。
2. 核心语法再拆解:diagram-design里必须掌握的四大表达
2.1 节点与连线:图表的“名词”与“动词”
在任何图表语言里,节点是最基础的单位,它对应的就是实体概念——一个系统、一个模块、一个人、一个决策点。连线则是实体之间的关系:服务调用、数据流转、审批通过、消息通知,等等。节点和连线的关系,可以理解成句子里面的名词和动词,没有它们,图表就啥也不是。
以Mermaid为例,最简单的流程图长这样:
graph LR A[用户请求] --> B[网关] B --> C[认证服务] C --> D[(用户数据库)]这段文本里有四个节点,三条连线,方向是LR(从左到右)。渲染出来就是一张非常标准的横向调用链图。这里有个容易被忽略的点:方括号和圆括号决定了节点的展示形状。方括号是矩形,圆括号是圆角矩形,还有个括号形状是六边形,可以用来表示决策点,比如:
graph TD A{是否通过校验} -->|是| B[进入业务逻辑] A -->|否| C[返回错误码]这行代码里,花括号“{}”表示菱形判断节点,连线上的“|是|”和“|否|”给关系增加了分支标签。这个表达能力,恰好覆盖了大部分流程图、状态图、时序图的场景。用熟了以后你会发现,绝大多数图表问题都只是在想清楚“有哪些实体、实体之间什么关系”这两个问题而已。
不过别急着炫技,我建议一开始不要把所有符号都背下来。把节点、连线、方向、标签这四个基础表达能力用熟,已经能覆盖日常工作里八成以上的图表需求。剩下的高级语法,都是按需查文档补充的,没人能一次全都背下来。
2.2 分组与容器:让图表的层级感立起来
节点一多,扁平排布就会变成一锅粥。20个节点平铺在画布上,光看连线就是一团乱麻。diagram-design给了两个层级组织工具:子图和分组。
子图在Mermaid里是这样用的:
graph TB subgraph 网关层 G1[API网关主节点] G2[API网关备用节点] end subgraph 服务层 S1[订单服务] S2[支付服务] end G1 --> S1 G2 --> S2子图的作用就是给多个节点圈一个可视化边界,渲染出来外面会多一个框,框上有标题。这一招在架构图里极其好使——你可以把“接入层”“服务层”“数据层”分别圈起来,整个系统的层次一眼就能看明白。相当于建筑图纸里的楼层线,让人能快速理解哪些组件属于同一逻辑分区。
分组则是另一种思路,在PlantUML或D2里用得更频繁,它不改变渲染布局,只是给一组元素打上标签,方便统一施加样式或者后续做过滤。在设计图表的时候,我习惯先把节点分成“核心调用链节点”“辅助监控节点”“外部依赖节点”三类,然后用颜色深浅区分主次。这样看图的人能快速把注意力放到主线,而不是被一堆旁支细节淹没。
2.3 样式与主题:视觉规范统一比想象中更重要
图表的“丑”和“乱”,本质上都是样式失控。色号五花八门、节点大小随缘、字体忽大忽小,图的信息密度再高,也会因为视觉噪音过大而丧失可读性。diagram-design工具天然适合做样式规范化,因为样式是写死在文本里的——比在GUI里手动点几百次鼠标要可控得多。
Mermaid里做条件样式大概是这个手感:
graph LR A[前端应用] -->|HTTPS| B[网关] C[监控系统] -.->|metrics| A style A fill:#e6f7ff,stroke:#1890ff style B fill:#fff7e6,stroke:#fa8c16给节点手动指定填充色和边框色,就能在一张复杂图里快速区分不同类型的模块。我自己的习惯是:核心业务组件用冷色系,外部依赖用暖色系,监控运维类组件用灰色系。这样一张图扫过去,第一眼看到的是核心链路,第二眼看到的是外部依赖边界,最后才查看监控细节。
另外一个容易被忽略的是“主题统一”。同一份文档里,五张图五种配色,给人的观感就是杂乱。建议在项目根目录里维护一个统一主题定义。Mermaid支持通过init指令设置主题颜色、字体、连线风格。团队完全可以约定一套标准:背景色、主色、连线粗细、字体大小全部写成固定配置,任何人画新图都基于这套配置开始。时间久了,整个文档站里的图表风格会收敛得非常好,专业度直接拉满。
2.4 自动布局与交互能力:工具帮你省掉最苦的活
刚切换到diagram-design方式时,我对自动布局是既爱又恨。爱的是不用手动挪节点了,恨的是自动布局偶尔会给出“正确但难读”的结果——比如一张三个子网互相调用的拓扑图,工具默认的排序可能会让连线交叉得很难受。
这时候需要理解一个底层事实:布局算法的本质是解决一个有约束的最优化问题,它优先保证节点不重叠、连线尽量短、层级尽量一致,但它并不知道你的业务语义里A和B的关系最重要。所以,diagram-design不是完全不需要排版思维,而是把排版从“体力活”降级成了“策略配置”。
遇到自动布局不理想的情况,我通常先检查有没有不合理的依赖关系,比如循环依赖会让引擎很难受;然后是明确方向,graph TD和graph LR的阅读顺序差异很大;再然后是考虑要不要拆图,一张图超过30个节点之后就别硬塞了,拆分成分层图和模块图效果更好。
至于交互能力,这是diagram-design另一个大杀器。基于Mermaid.js可以在HTML里生成支持点击跳转的图节点,点击某个服务节点,可以跳到对应的监控面板或代码目录;支持hover高亮相邻节点;还有一键展开折叠子图的交互插件。这些能力,传统静态导出的图片是完全无法匹敌的。在技术文档里插入一张可交互的架构图,读者的体验和看一张jpeg截图完全是两码事。
3. 完整实操:从零搭一套可复用的diagram-design方案
3.1 工具选型:Mermaid、PlantUML、D2到底选哪个
聊完思路和语法,到了选型环节。这个决策会直接影响团队的接入成本,值得认真比较。市面上主流的diagram as code工具就那么几款:Mermaid、PlantUML、D2,还有AntV X6这类更偏重前端交互的库。我的建议是——大多数技术团队直接选Mermaid,理由我在下面详细说。
先放一张对比表,方便一览:
| 维度 | Mermaid | PlantUML | D2 |
|---|---|---|---|
| 学习曲线 | 低,Markdown风格 | 中,伪代码风格 | 低,声明式语法 |
| 原生渲染场景 | 流程图、时序图、甘特图、状态图、饼图等 | UML全系列,面向软件设计更专业 | 架构图、网络拓扑、通用图表 |
| 生态绑定 | GitHub、GitLab、Notion、语雀都原生支持 | Jenkins、Confluence等老牌平台支持好 | 年轻,但CLI体验出色,有Terraform集成 |
| 中文支持 | 需要设置字体,大部分情况OK | 需要处理字体,容易乱码 | 默认支持,体验较好 |
| 扩展性 | 有丰富的插件,支持自定义主题和交互 | 偏向静态图片导出 | 支持多文件、变量、布局参数,自动布局很优秀 |
| 适合场景 | 写Markdown文档为主的团队 | 软件设计文档和UML重度用户 | 对布局和视觉细节挑剔的团队 |
对比完你会发现,Mermaid赢在生态和上手速度。GitHub直接渲染、Notion里写代码块就能出图、VS Code装个插件就能预览,这种“写文档随时配图”的顺畅感,是其他工具很难比拟的。如果你团队已经重度使用Markdown,Mermaid就是零成本接入。
PlantUML的强项在于UML语义更严格。如果你们要做严肃的类图、时序图、部署图,PlantUML的那些语法更接近UML规范。但我个人用下来的感受是,它的语法略微啰嗦,而且默认排版风格比Mermaid老旧一些。
D2是很值得关注的新生代工具。它的布局引擎是我见过的默认效果里最好的,连线基本不交叉,尤其是画网络拓扑和系统架构图的时候,视觉质感明显比Mermaid强一个档次。但它生态还在积累中,GitHub等平台不能原生渲染,团队接入需要额外搭一套CI渲染管线。
3.2 实测流程:一条命令把文本变成图
这里我以Mermaid为例,走一遍从零到一的全流程,因为它在终端里的体验足够轻量。你不用先搭什么重型平台,只要有一个装了Node.js的环境就能开始。
第一步,全局安装Mermaid的命令行工具:
npm install -g @mermaid-js/mermaid-cli第二步,写一个最简单的图表文件,命名为architecture.mmd:
graph TB subgraph Client Web[Web前端] Mobile[移动端] end subgraph Server API[API服务] Auth[认证服务] end DB[(PostgreSQL)] Web --> API Mobile --> API API --> Auth API --> DB Auth --> DB第三步,渲染成PNG:
mmdc -i architecture.mmd -o architecture.png -b white如果一切顺利,当前目录下会生成一张排版整齐的架构图。这张图的整个生命周期都在这一个文本文件里,你把它放进Git仓库,团队里的任何人随时可以clone下来改、重新渲染、对比历史版本。
这里我踩过一个小坑:新装的mmdc第一次渲染,可能会因为缺少Chromium而报错,因为底层是无头浏览器渲染的。解决方案是装一下依赖:
npx puppeteer browsers install chrome或者用系统自带的Chrome指定路径:
mmdc -p puppeteer-config.json -i architecture.mmd -o architecture.png其中puppeteer-config.json里可以指定executablePath指向你本机装好的Chrome。这种事情看起来很小,但第一次配置的时候容易卡住半小时,提前知道能省不少力气。
3.3 进阶玩法:把图表接入文档站和CI/CD流水线
单机渲染只是入门,diagram-design真正发挥威力在集成环节。
首先是文档站集成。如果你的团队用VuePress、Docusaurus、MkDocs这类静态站点生成器,Mermaid基本都是原生支持或有一等插件。VuePress里最简单的启用方式是在config.ts里加:
markdown: { mermaid: true }然后在Markdown里直接写:
## 系统架构图 ```mermaid graph LR A[前端] --> B[网关] --> C[服务] ```页面构建的时候,图会被自动渲染成SVG,读者看到的不再是静态截图,而是可缩放、可点击的活图。这里我强烈建议打开交互式功能,读者在文档里点击节点跳转到对应代码模块,体验真的会提升一大截。
然后是CI/CD校验。图也是代码,那它就应该过lint和解析检查。在我们的工程实践里,把图表检查并入了PR流水线:每当有.mmd或者Markdown文件变动,CI就跑一遍语法检查,解析不通过直接让PR失败。做法是在GitHub Actions里加一个步骤:
- name: Validate Mermaid diagrams run: | for file in $(find docs -name '*.mmd'); do mmdc -i "$file" -o /dev/null done这个步骤看起来简单,但能挡住绝大多数手误——比如漏了一个括号、写错了一个箭头方向。更重要的是,CI里也顺便生成了最新版的PNG图片产物,直接发布到文档站,确保文档站上永远展示的都是“最新且合法”的图。这已经不只是画图工具了,而是图表治理体系的一部分。
再分享一个我们正在用的技巧:结合Git subgraph做图表的“多环境”管理。比如同一个部署架构,开发环境、测试环境、生产环境只是节点数量不同。不用维护三份文件,而是把公共部分抽成一个基础文件,再用Mermaid的!include指令引入模板块,加一层变量替换就生成不同环境的图。这样架构图跟着环境配置走,永远不会出现“开发环境更新了生产环境还是旧的”这种问题。
4. 常见问题与排查技巧实录
4.1 语法看着没问题,渲染出来却错乱
这是diagram-design新手上路最常见的挫败点。文件里的字都打对了,箭头方向也没毛病,但渲染出来的图就是“丑”,要么连线交叉,要么层级错位。
先说一个最容易忽略的原因:方向声明。Mermaid里的TD、TB、LR、RL不是装饰,它决定引擎的布局方向。默认的TD(从上到下)适合流程图,但画架构图时,我通常会改成LR(从左到右),因为大部分系统的数据流是从上往下还是从左往右,跟你描述的边界高度相关。一个常见的错误是把一个带横向依赖的图配了TD方向,引擎被迫把所有节点按纵向堆叠,结果自然是天女散花。
接着是子图与节点归属混乱。子图嵌套子图、节点跨子图连线,这些都会显著增加布局难度。引擎不是不能处理,而是处理得比较吃力。建议是:子图之间尽量通过子图入口节点连线,不要在子图内部直接调用另一个子图内部节点。这就像模块化代码一样,保持接口干净,布局引擎才会给你干净的排布。
还有一种情况比较隐蔽:使用特殊字符没转义。中文括号、一些特殊符号,尤其是带引号或冒号的节点文本,尽量用引号包起来或改成全角符号,否则解析器可能意外截断文本。我在写数据库节点的时候经常出现这种问题,比如DB[(PostgreSQL: 主库)],里面的冒号容易触发解析异常,要么报错,要么渲染残缺。规避方法是简单写DB[(PostgreSQL主库)],不写冒号。
4.2 中文显示乱码或字体发虚
很多从英文文档切过来的人,第一次渲染带中文的图就踩坑了。Mermaid CLI默认加载的字体可能不支持中文,渲染出来要么是方块、要么是乱码。这个问题不是Mermaid专属,PlantUML、D2多多少少都有。
解决思路无非两种:要么指定系统里已有的中文字体,要么把需要的字体文件嵌入到PUPPeteer渲染环境里。第二种比较可控,我推荐。在项目里放一个puppeteer-config.json:
{ "args": ["--no-sandbox", "--font-render-hinting=none"], "executablePath": "/usr/bin/google-chrome" }然后在CSS文件里定义好字体栈:
@font-face { font-family: 'NotoSansSC'; src: url('./fonts/NotoSansSC-Regular.otf') format('opentype'); } /* 或者直接用系统字体 */ :root { --mermaid-font-family: 'NotoSansSC', 'PingFang SC', 'Microsoft YaHei', sans-serif; }更省事的方式是直接用系统字体:把mmdc命令的配置里加一个--font-family参数,指向系统已装好的中文字体。在Linux服务器上,一般装一下fonts-noto-cjk就解决了。
4.3 单张图规模失控:拆解还是压缩
一个场景我已经踩过三次了:架构图越画越大,最后变成一张巨型地图。节点四五十个、连线上百条,渲染SVG文件能达到几兆,打开文档页面浏览器都卡顿,自动布局完全失效,图的可读性几乎为零。
这个时候最有效的操作不是“继续调样式”,而是“拆分语义层”。我的拆分原则是:
| 图表类型 | 合适规模 | 超出后的建议 |
|---|---|---|
| 系统架构全景 | 15-25个节点 | 拆成“接入层/平台层/数据层”三张图 |
| 时序图 | 5-8个参与者 | 按业务场景拆成多张场景图 |
| 流程图 | 不超过15个节点 | 拆成主流程+异常分支两张图 |
| 数据模型ER图 | 不超过20张表 | 按限界上下文拆成多个领域图 |
拆分之后,再用链接把各张子图串起来。比如在全景图里的某个子模块节点上加上点击链接,指向对应的详细图。Mermaid支持给节点设置click事件,这样既保持了全局视野,又能下钻到细节。这才是架构文档该有的交互形态。
还有一种做法是利用Mermaid的zoom插件,让用户可以在页面上缩放查看大图。但这只是治标,图上承载信息太多,读者还是抓不住重点。记住一句话:一张图讲一个故事,讲得好比讲得多重要得多。
4.4 多人协作时如何维护图表规范
diagram-design落地之后,最大的挑战不是技术,而是“人的一致性”。团队里有5个人都在用,每个人写的图风格都不一样,有的用TD有的用LR,有的喜欢在节点里写英文有的写中文,时间长了文档库还是一片混乱。
我的做法是三层规范:
第一层是项目模板。每个人新建图表文件时,直接以团队样板文件为起点,里面已经预置了主题、方向、常用子图结构。人都有惰性,给模板比给规范好用得多。
第二层是自动检查。CI里不只是跑语法检查,也跑风格检查——节点是否都给出了明确文本、是否使用统一颜色映射、连线是否有标签。这些可以写成一个简单的脚本,解析.mmd文件做规则检查,不通过的给出警告。虽然不能做到100%自动化,但能在流程上逼一下。
第三层是定期Review。我每两个星期会做一次图表专项Review,把新增和修改过的图全部过一遍,重点检查两件事:图的主题是否符合当前系统现状;图的表达方式是否符合团队的阅读习惯。这个Review的重要性被很多人忽略——图是写给人看的,定期站在读者角度去审视,才能让规范真的发挥作用。
5. 一点真实的项目体会
做diagram-design这套体系不是一蹴而就的。最开始我也只是在文档里零星嵌入几张图,觉得方便。后来在一次大版本重构中,需要同时产出十几张架构图和流程梳理,我把整套流程打磨成了现在的标准化方案。从那以后,我对“图即代码”的理解开始变得具体:它的价值不是省掉拖拽鼠标的动作,而是彻底改变了图表在团队协作中的生命周期。图不再是某个人的个人产物,而是像代码一样可以被检视、被评审、被追溯。
如果让我给刚接触diagram-design的人一句最实在的建议,那就是:先不要追求语法全面覆盖,也不要急着搭复杂流水线。挑一个你手头正在写的文档,把里面需要画图的部分用Mermaid重写一遍,渲染出图,看看效果,感受一下“改一行文本、图就变了”的过程。这种即时的正反馈,比我在这里说一万字都管用。等你在两三个项目里都用顺了,再回头去考虑主题统一、CI集成和团队规范。工具是可以替换的,工作流才是真正的复用资产。diagram-design真正教会我的,不是怎么画图,而是怎么让图在项目里活起来,并且持续保鲜。