1. 项目定位为什么“画图”这件事值得用代码重做一遍先说个扎心的事实大多数开发团队里的架构图、流程图、时序图画完第一版之后基本就进了坟墓。需求一变文档没人更新图还是三个月前那张老图。而“diagram-design”这个项目的核心思路就是反过来——不直接“画”图而是用文本/DSL领域专用语言去“描述”图然后再通过渲染引擎把它变成可视化的图形。说得更直白一点像写代码一样画图像review代码一样review图。这套思路其实不是什么新发明。类似的做法早就在基础设施和开源圈子里流行比如用PlantUML画UML、用Graphviz画依赖图、用Mermaid画流程图和时序图。但真正值得认真落地为一个独立项目的原因在于它解决了几件非常实际的问题图的可版本化。文本进Git每次更新有diff能追溯能回滚不再是一张png孤零零躺在网盘里。图的可协作。多人改一个文件合并冲突时Git能告诉你哪里改过而不是大家各自导出图片然后再手工拼。图的可再生。一个源文件可以生成不同风格的输出比如白底版、深色版甚至像官网那样按主题自动换肤。所以如果你想搭建一套靠谱的“文档即代码”工作流或者你正在思考如何让团队里的架构图、流程图真正活起来那么这篇文章就是围绕“diagram-design”这个项目做的一次完整拆解从选型、语法、流程到踩坑一次讲透。我尽量不写那些“官网文档翻译”式的教程而是把实际项目里用得到的东西、以及那些文档里不会告诉你的细节揉碎了讲出来。你会看到完整的代码示例、配置决策过程以及我在真实项目里撞见过的问题。2. 选型阶段Mermaid、PlantUML还是Graphviz怎么选才不后悔正式开始写图之前最绕不开的一步是选渲染方言。很多新手会一上来就纠结“哪个工具最好”但我建议先明确一个判断逻辑语法表达的舒适度、渲染输出的可控程度、以及生态接入成本三者取平衡而不是单纯比某个功能。2.1 三类主流方言的核心差异拿Mermaid、PlantUML和Graphviz做对比你会发现它们的侧重点完全不同工具语法风格擅长场景渲染质量社区活跃度上手难度Mermaid极简、类Markdown流程图、时序图、状态图、饼图中等偏清爽非常高低PlantUML类Java风格UML全系类图、时序图、用例图中高偏工程风高中Graphviz声明式属性强依赖图、树形结构、自动布局高偏学术论文风高较高主观感受是Mermaid看起来最接近“自然语言”写起来几乎不需要心理负担比如A-B这种箭头写法非常直觉。PlantUML则更适合UML重度用户尤其需要严格表达类之间的关系、接口实现、多重继承这类建模语义时PlantUML的语法更完善。Graphviz则适合节点多、连接关系复杂的场景它的自动排布算法在整体布局均匀度上是三者中最好的因为它本质是专业级图布局引擎。2.2 为什么我在项目主体里以Mermaid为默认参考这次项目里我选Mermaid做主力理由其实很朴素团队里大多数人不愿意学一套复杂的建模语言但他们能秒懂Markdown风格的写法。Mermaid对中文支持相对友好GitHub仓库、各类Markdown编辑器、很多文档平台都原生渲染。站在“让图真正被大家使用”的角度这个生态优势无价。它有基础的样式调整能力配合主题和CSS足够撑起日常80%的需求。当然这不代表PlantUML和Graphviz没有用。实际情况是后续如果做更严谨的领域建模我会把PlantUML单独作为补充方案如果涉及复杂网络拓扑、分层依赖关系自动化生成Graphviz反而更有优势。工具不是越多越好而是要在合适场景用合适方言。注意如果你所在团队已有某种方言的沉淀比如代码仓库里全是PlantUML文件那就不建议强行切换。迁移成本永远比“更好的语法”更贵。2.3 文本绘图核心三步走不管用哪个方言整个工作流的逻辑是一样的编写源文件所有图形描述放在一个目录下命名清晰按模块或系统组织。渲染出图通过命行工具或脚本将源文件转为PNG、SVG、PDF等格式。集成发布嵌入到文档站、Wiki、README或CI产物中作为文档体系的一部分。这套流程在“diagram-design”里的具体落地我会在第4节里用一个完整示例展开。3. 核心语法细节节点、连线与子图以及那些文档没讲透的坑很多人学图方言只看到“A-B”这种箭头然后觉得很简单。但真正进阶的部分在怎么组织节点语义、怎么分块布局、怎么处理复杂关系以及怎么使用图元属性控制最终渲染结果。单独把语法过一遍没意义我把常用的核心概念拆出来结合踩坑经验讲。3.1 节点的定义与语义命名Mermaid里定义一个节点非常容易比如flowchart LR A[登录请求] -- B[校验] B -- C{是否合法}但这里有个数据实践层面的建议不要直接用“A”“B”“C”这种抽象字母而是使用语义化ID。比如flowchart LR LoginRequest[登录请求] -- AuthCheck[凭证校验] AuthCheck -- Decision{是否合法}为什么因为源文件本身就是文档它不只是一张图的“底稿”而是要被人读的。如果你把ID写成login_request那么别人在阅读源文件时即使不看渲染结果也能大概猜到流程内容。更重要的是在Git diff的时候语义化ID能让你清楚地看出来是哪个环节发生了变更而不是“A改成了BB改成了C”这种废话式变更。3.2 连线类型不是摆设Mermaid支持多种箭头类型例如--实线箭头表示数据流/流程方向---实线无箭头表示关联-.-虚线箭头表示异步或间接依赖--双向箭头表示双向交互粗线箭头表示重点路径或主流程在真实项目中明确区分连线类型会让图的表达力提升一个档次。例如支付请求走强调主链路风险相对高回调通知走-.-标识异步事件读取配置表走---表示非关键关联。这样辅助图和文档的优势就出来了读图的人不仅看“谁指向谁”还能看出依赖的强度与类型。3.3 子图Subgraph的布局控制与分组当图越来越复杂大量节点扁平堆在一层布局会乱到你怀疑人生。这时就要引入子图把语义上靠近的节点归拢在一起flowchart TB subgraph 网关层 A[API网关] -- B[鉴权服务] end subgraph 业务层 C[订单服务] -- D[库存服务] end B -- C这里有几个值得注意的点第一子图会影响连线层次。如果只把B和C放在子图里但A放在外面连线跨层时布局引擎处理效果往往不理想。一般来说让子图内部的连线保持在内层子图之间只留少量跨层连线布局会干净得多。第二子图的顺序由声明顺序决定。在Mermaid中子图的渲染顺序大体上跟着定义顺序走所以如果你想让“网关层”出现在上方就先定义它。第三子图内也可以继续嵌套子图但层级不要超过3层。超过三层渲染出来基本就是一团乱麻。如果你发现需要用嵌套子图表达系统复杂度那大概率不是图的问题而是架构本身需要拆分文档了。3.4 用配置项和主题统一视觉风格“diagram-design”里最容易被人忽略的是主题配置。默认的Mermaid配色其实还算顺眼但放在实际文档体系里会有强烈的“工具默认感”。好在Mermaid支持初始化配置可以高度自定义%%{init: {theme: base, themeVariables: {primaryColor: #d2eaff, lineColor: #666, textColor: #333}}}%% flowchart LR A[节点一] -- B[节点二]这种初始化注释支持写在源文件顶部。主题变量可以控制节点填充色、边框、连线颜色和字体色。建议团队把统一主题变量抽成一个公共配置文件渲染时通过命令行参数注入而不是每个源文件都写一遍。这样后续改色只需要改一处。另外值得提醒的是不要过度调样式。图的核心是信息结构清晰不是视觉炫技。大多数情况下保持统一配色、让节点内容规整比在每个小节里换五种颜色更有用。3.5 关于中文乱码和字体这是绝大多数中文用户都会碰到的第一个坑。在浏览器预览或者某些在线编辑器里中文显示可能没问题但一旦用命令行工具导出PNG很容易出现豆腐块或字体缺失。原因在于渲染引擎的字体配置没有正确覆盖中文系统字体。解决方案通常是在导出命令中增加字体配置参数或在源文件中指定字体族。以常见的Mermaid CLI为例puppeteer渲染模式下需要确保系统安装了相应中文字体如思源黑体、Noto Sans CJK SC并且在配置里指定fontFamily否则就会退化为不可用字体。这点我在第5节会继续展开。4. 完整实操从零搭建一个可复用的图文档体系这一节我想以一个模拟的“某跨平台系统”数据架构为例带你完整走一遍从零搭建图文档体系的过程。这个系统的核心流程包括客户端上报数据、网关接收、消息队列缓冲、数据处理管道、最终入库。整个过程既有流程结构又有组件依赖非常适合用来演示。4.1 建立目录结构与命名约定第一步先为所有图表源文件建立统一目录docs/ ├── diagrams/ │ ├── system/ │ │ ├──>flowchart LR subgraph 客户端 Mobile[移动端] -- Reporter[数据采集模块] Web[Web端] -- Reporter end subgraph 接入层 Reporter --|HTTPS上报| Gateway[API网关] Gateway -- RateLimiter[限流组件] RateLimiter -- Validator[参数校验] end subgraph 数据处理层 Validator -- MQ[(消息队列)] MQ -- Consumer[数据消费服务] Consumer -- Transform[清洗与转换] end subgraph 存储层 Transform -- DB[(主数据库)] Transform -- Warehouse[(分析仓库)] end DB -- Metrics[监控大盘] Warehouse -- Metrics写这一段时我有几个操作心德第一不要把环节拆得过细。我当时随手写的时候一开始把“负载均衡”“DNS解析”“TLS终止”都拆成了独立节点结果图变得很长很难看。后来果断把网络层那些细节合并为“API网关”的一部分用连线标签“HTTPS上报”表达要点结构立刻清爽。图的表达能力不在于“节点多”而在于“边界清晰”。第二子图里节点命名尽量简洁。子图标题可以写完整业务名但节点文本保持在4到8个字以内过长时渲染效果会明显变差。第三跨子图连线是注意力焦点。图上所有连接不同子图的线都是架构上的边界交互。像上面示例中的 “Gateway - RateLimiter”和“Validator - MQ”才是最重要的关键链路值得在正文里单独用文字解释。4.3 为关键业务流程补充时序图架构图表达的是全局但很多需求细节是时序图才讲得清的。同一个系统里我会为高风险的交互过程单独画时序图。例如一次下单操作的完整时序sequenceDiagram participant U as 用户 participant P as 订单页面 participant O as 订单服务 participant S as 库存服务 participant D as 支付网关 U-P: 点击下单 P-O: 创建订单请求 O-O: 校验商品状态 O-S: 预占库存 alt 库存余量足够 S--O: 预占成功 O-D: 发起支付 D--U: 收银台跳转 else 库存不足 S--O: 预占失败 O--P: 返回库存不足提示 end时序图的表达逻辑非常接近代码执行顺序写起来几乎等同于“用文字描述一次函数调用链”。它的好处是能清晰暴露异常分支避免“只画了happy path”的通病。我特别推荐把“鉴权”“支付”“退款”这类涉及多方协作的关键链路画成时序图因为在代码Review的时候时序图比一堆代码注释更能帮助同事快速理解交互逻辑。4.4 本地渲染与CI集成源文件写好了接下来就是渲染。本地命令行手动渲染尤其是调试配色或字体时很依赖CLI工具一般操作是npx mermaid-js/mermaid-cli -i>npx mermaid-js/mermaid-cli -i>render: mkdir -p dist for file in docs/diagrams/**/*.md; do \ npx mermaid-js/mermaid-cli -i $$file -o dist/$$(basename $$file .md).svg -t base -w 1200; \ done虽然这个脚本很简单但它做了一件事把复杂的命令封装成一行统一入口避免团队里三个人用三种参数产出的图风格不一致。更进一步可以把这个渲染步骤挂到CI里。在每次合并到主分支时让拉取请求自动生成最新版图片加到一个构建产物里。这样文档和代码的更新永远保持同步而不是“代码改了一周文档还在旧分支”。注意CI环境里需要安装对应字体否则中文会乱码。常见的做法是在构建镜像里安装fonts-noto-cjk这类字体包或在runner启动脚本中提前执行字体安装。4.5 转换成其他图类型不只是流程图“diagram-design”这个项目里我还会把同一套数据模型转成不同视角的图。比如把系统模块间的强依赖关系用ER形态表达或者把榜单排名用简单的饼图/柱状图表达。这不只是为了炫技而是为了在“不同场合有不同表达方式”给架构评审人员看的数据流转图核心是子图边界和跨边界连线给研发排查问题时的时序图强调消息顺序和返回路径给管理层汇报用的统计示意图强调关键数字和简单对比。同一个底层数据对象通过不同DSL表达成不同体裁这种灵活性是手绘工具很难具备的。5. 高频问题与排查方法这个部分是我整理的真实项目中的高频问题每一个都是我或者团队同事实际操作中撞过的墙按优先级列出来。5.1 中文乱码问题现象在本地预览正常用CLI导出PNG后中文变成方框或乱掉。原因渲染进程通常是无头浏览器没有找到可用的中文字体。排查步骤在终端输入fc-list | grep -i cjk看看系统是否有CJK字体。如果没有安装一个如fonts-noto-cjk。如果字体已安装还乱码检查Mermaid配置中的fontFamily是否设置了可用的中文字族。可以在初始化配置里写上%%{init: {theme: base, themeVariables: {fontFamily: Noto Sans CJK SC}}}%%注意浏览器里的字体回退逻辑和命令行里的回退逻辑不一定一样所以“本地预览没问题”并不能说明渲染环境没问题。5.2 布局乱跑子图不按预期排布现象同样一张图本地渲染和团队同事渲染出来的布局不一样或者稍微加一个新节点整张图的形状就变得很怪。原因Mermaid的自动布局算法对节点顺序、连线方向、子图嵌套层数都很敏感。某些节点虽然逻辑上可以指向上游但从布局引擎角度看它可能会被强制拉平或移动。排查方法优先统一渲染引擎版本。团队里有人用旧版本有人用新版本布局一定不一致。把CLI版本号写进构建脚本里锁死。如果某条线的方向严重干扰布局考虑把它从子图内部挪到子图之间让边界的跨度变大反而更容易布局。如果确实需要非常精确的坐标控制比如像素级对齐不要企图靠Mermaid塞坐标实现那说明你进入了Graphviz的领域或者直接用绘图软件更适合。5.3 图太复杂阅读成本急剧上升现象节点超过20个连线超过30条图完全失去表达力。判断原则一张图如果需要在盯着屏幕超过30秒才能理解主干流程那它就该拆了。解法把大图按领域拆成多个小图每个图表达一个职责域用子图把复杂度包住让读者先看边界再看边界之间的连线对于“当前关键路径”在正文里用文字或高亮颜色强调而不是试图让图把所有信息都装下。我个人经验架构图“节点数小于15”是可读性边际线超过这个数维护成本和理解成本都会陡增。一个普通读者扫一眼只能记住三四个重点因此最好每张图只承载一个明确的问题视角。5.4 多人协作时的版本冲突与解耦现象两个人同时改同一个图文件Git冲突无解。原因图的源文件虽然是文本但表达的是全局结构大家很容易在同一处区域里增删节点。缓解手段尽量一个文件只承载一个主题并列关系用多个文件承接降低多人同时修改同一个文件的可能性。冲突产生时优先沟通而不是直接“用我的版本覆盖”。因为图的语义冲突和人之间的理解冲突高度相关没有哪个自动合并工具能替你们做架构决定。把图的负责人机制写进目录README里明确“这张图谁负责维护”减少无主文件的混乱状态。5.5 输出图片模糊、尺寸不统一现象README里嵌入的图有些清晰有些模糊大小还参差不齐。原因多半是因为输出的位图分辨率不够或者宽度参数没有统一。解法一律输出SVG不输出PNG。如果目标平台强制PNG那么在导出时把-w参数调大比如1600或2000不要依赖800默认值。在Markdown中建议限制图片展示宽度而不是缩放原图例如使用{width800}的方式既清晰又统一。6. 常用参数与配置速查表这是我在实际项目中沉淀的一份配置速查你可以直接抄作业。因为有些参数不是每次都记得住我干脆都在这个表里统一管理。项目配置或命令备注主题定制%%{init: {theme: base, themeVariables: {primaryColor: #fff}}}%%自定义变量时优先基于base背景颜色themeVariables: {background: #ffffff}深色模式下另配边距控制themeVariables: {nodeMargin: 8}数值越大节点间距越大主节点填充色themeVariables: {primaryColor: #f0f4ff}用十六进制色值文字颜色themeVariables: {textColor: #333}与背景色做好对比度字体themeVariables: {fontFamily: Noto Sans CJK SC}中文场景必配输出SVG-o 输出.svg推荐默认输出PNG-o 输出.png -w 1600目标平台不支持SVG时用指定主题-t base可用base/neutral/dark/default锁定布局方向flowchart LR/TB/RL/BT在flowchart后定义启用Markdown渲染markdownScripts: true在部分CLI版本中支持补充两个细节。第一个配置初始化块写在文件首行时如果同一文件有多个%%{}%%后面的会覆盖前面的配置不要写多个混淆。第二个修改主题颜色的时候注意primaryBorderColor和lineColor对整体观感影响很大边框颜色太深会让图显得很脏。建议色值饱和度降低一点视觉上更耐看。快速调试表示例前面出现过的数据流图的深色模式版本%%{init: {theme: base, themeVariables: {background: #1e1e1e, primaryColor: #2d2d2d, primaryBorderColor: #555, lineColor: #999, textColor: #ddd, fontFamily: Noto Sans CJK SC}}}%% flowchart LR A[数据上报] -- B[网关] B -- C[(消息队列)]
阅读完成 · 觉得有帮助?