资讯动态

VSCode+Graphviz:用DOT代码化绘制流程图与工程化导出

发布时间:2026/9/17 6:13:32 来源:尧图企业网站定制
很多同学第一次被要求交一张流程图的时候第一反应都是打开拖拽工具画几个框连几条线然后花两个小时对齐箭头。我不是说拖拽工具不好而是当你需要维护几十张图、每次需求变更都要重画、还要放进文档和代码仓库里跟版本走的时候拖拽这件事就变成了负担。VSCode 加 Graphviz 这套组合核心思路是把图当成代码来写。你只描述谁连谁、什么形状、什么优先级坐标、分层、连线走向交给布局引擎去算。这篇文章就围绕 VScode 里的 Graphviz 插件展开从引擎安装、插件挑选、DOT 语法、流程图形状语义一直讲到用户管理模块流程图的完整实操与导出参数计算顺带把我踩过的中文乱码、节点重叠、集群被拉散这类坑一次性说完。不管你是刚接触 Graphviz 的新手还是已经能写几行 DOT 但总嫌图丑的老手这篇都能直接抄作业。1. 为什么我最终把流程图工具换成了 Graphviz1.1 拖拽式画图的三个真实痛点先说我自己的经历。早些年做后台系统设计每个模块一张流程图用拖拽工具画完导出 PNG 塞进文档。前两个月挺爽第三个版本开始就崩了产品经理加了一个二次验证环节我要把这一个框插进原有链路结果下面所有节点全部要手动往下挪挪完发现箭头没跟着动重新连一遍再对齐一次一张图四十分钟。这是第一个痛点——改一处动全图。第二个痛点是版本管理。拖拽工具的源文件基本是二进制或者一大坨结构化 XML提交到仓库里diff 出来全是乱码同事 review 的时候只能看导出的图片根本没法判断你改了哪条线。第三个痛点是复用新项目要画一个几乎一样的登录流程你只能把旧图另存一份再一个个框去改文字改完忘了改标题文档里就出现图书管理系统下面写着用户登录的尴尬。1.2 DOT 语言的核心思路把布局交给算法Graphviz 走的是完全不同的一条路。它定义了一门叫 DOT 的描述语言你写的不是这个框放在 x120, y340而是节点 A 指向节点 BA 是菱形边上写是。真正的坐标计算由布局引擎完成。以流程图默认使用的dot引擎为例它的分层布局思路大致分四步打破环有向图里如果存在 A→B→C→A 这种回环先临时反转某些边把图变成无环结构分层按边的方向把节点分配到一层一层的 rank 里一条边默认跨一层层内排序调整同一层里节点的左右顺序目标是把交叉的边数降到最低分配坐标最后才根据节点尺寸、nodesep、ranksep这些参数算出每个节点的实际像素位置。理解这四步特别重要因为后面所有图丑的问题本质上都是你没给算法足够的约束信息它在按自己的最优解排而它的审美跟你的预期不一致。想让它听话你要用ranksame、weight、constraint、不可见边这些手段去引导。1.3 三套方案横向对比我为什么选 DOT维度拖拽式工具MermaidGraphviz DOT输入方式鼠标拖拽文本Markdown 内嵌文本独立 .gv 文件布局控制力手动完全可控但费时弱几乎不能干预布局强几十个布局参数可调版本管理友好度差好好纯文本可 diff复杂图表现力强一般节点多了容易乱强支持集群、端口、HTML 表格标签与 VSCode 集成一般内置预览插件实时预览 一键导出批量生成能力几乎没有需要额外工具链极强一条命令出多种格式Mermaid 胜在写文档时随手就能嵌但它的布局干预能力太弱节点一多就控制不住拖拽工具胜在所见即所得但改起来累。Graphviz 的定位很清楚——你愿意花二十分钟学一门小语言换来后面几年画图的效率。而且它的输出格式极多PNG、SVG、PDF、甚至 JSON 描述文件都能出导出 SVG 放进技术文档里放大不糊。2. VSCode 与 Graphviz 环境搭建先装引擎再装插件2.1 顺序千万别搞反引擎是本体的本体新手最容易犯的错是先装插件然后发现预览一片空白或者报一个spawn dot ENOENT。原因很简单VSCode 插件只是个壳真正干活的是本机的 Graphviz 引擎。插件做的事情是把你的 .gv 文件内容丢给引擎拿回渲染结果再显示。所以第一步永远是装引擎。Windows 上到 Graphviz 官网下载页拿最新的安装包运行安装向导时注意勾选Add Graphviz to the system PATH for all users这类选项这是后面能不能直接调用dot命令的关键。默认安装路径一般是C:\Program Files\Graphviz\bin。macOS 用 Homebrew 一行搞定brew install graphvizDebian 系发行版sudo apt update sudo apt install -y graphviz装完必须验证这一步别省dot -V正常会输出类似dot - Graphviz version 12.x.x的版本信息。如果提示找不到命令Windows 用户九成是 PATH 没生效关掉所有终端和 VSCode 重开一次还不行就手动把C:\Program Files\Graphviz\bin加进系统环境变量。macOS 用户如果是 Apple Silicon注意确认/opt/homebrew/bin在 PATH 里。注意Windows 安装完成后务必重启 VSCode 而不是只重载窗口。环境变量的读取发生在进程启动时重载窗口不会刷新 PATH。除了dot引擎顺手确认一下neato、fdp这几个命令也在同一目录下后面画无向关系图可能会用到。2.2 VSCode 侧插件怎么选我常用这几个打开扩展面板搜索 graphviz会出来一屏结果。别全装按需选两三 个就够了。下面是我实际在用、也推荐给同事的组合插件主要作用我的使用场景Graphviz (dot) language supportDOT 语法高亮、括号匹配、代码片段必装纯写代码也舒服Graphviz Interactive Preview侧边栏实时预览、缩放拖拽、直接导出日常主力改一行右边立刻变Graphviz Preview提供预览命令与按钮轻量替代方案占用小Graphviz Markdown Preview渲染 Markdown 里的 graphviz 代码块写设计文档时用不同插件的功能有重叠装一个预览类的就够了装多了会有右键菜单打架的情况同一个命令出现好几个入口。插件名称和作者以扩展面板实际搜索结果为准更新比较频繁。交互式预览那个插件有个我很喜欢的细节它把渲染视图放在侧边栏的独立面板里支持鼠标滚轮缩放和拖拽平移图大了也不用眯着眼看。导出按钮可以直接把当前视图存成 SVG 或 PNG省得去命令行敲。2.3 路径配置与第一个能跑起来的例子现在 VSCode 里新建一个文件命名为demo.gv。这里插一句关于扩展名的经验.dot也能用而且在不少编辑器里默认关联到 Graphviz 语法但.dot在 Windows 上会和 Word 的模板文件扩展名撞车双击容易打开错程序。我现在的习惯统一用.gv清晰且不冲突。粘贴下面这段最小示例然后用预览命令打开digraph demo { rankdirTB; node [shapebox, stylerounded,filled, fillcolor#eef4fb, color#4a7ebb]; edge [color#6b7a8d, arrowsize0.8]; start [shapeellipse, label开始]; step [label读取输入]; judge [shapediamond, label校验通过]; done [shapeellipse, label结束]; start - step; step - judge; judge - done [label是]; judge - step [label否]; }如果预览正常出图说明插件和引擎已经通了。如果预览报错提到找不到 dot就去设置里搜 graphviz找到形如Dot Path或Engine Path的配置项填上引擎的绝对路径Windows 下大概是C:\Program Files\Graphviz\bin\dot.exe。配置项的键名不同插件略有差异认准让插件知道 dot 在哪这一件事就行。想更进一步的话可以直接在settings.json里写死方便团队统一{ graphviz-interactive-preview.preview.enginePath: C:/Program Files/Graphviz/bin/dot.exe }如果你的插件版本用的键名不同别硬套在设置界面输入 graphviz 看提示。2.4 把 dot 命令做成 VSCode 任务一键出图预览只能看交付要的是文件。与其每次切到终端敲命令不如把它写进 VSCode 的任务系统。在项目根目录建.vscode/tasks.json{ version: 2.0.0, tasks: [ { label: dot: 导出 SVG, type: shell, command: dot, args: [-Tsvg, ${file}, -o, ${fileDirname}/${fileBasenameNoExtension}.svg], problemMatcher: [], group: { kind: build, isDefault: true } }, { label: dot: 导出 300dpi PNG, type: shell, command: dot, args: [-Tpng, -Gdpi300, ${file}, -o, ${fileDirname}/${fileBasenameNoExtension}.png], problemMatcher: [] } ] }之后在 .gv 文件里按CtrlShiftBmacOS 是CmdShiftB就能直接生成同名 SVG。这个 ${file} 变量是当前打开的文件${fileBasenameNoExtension} 是不带扩展名的文件名所以不管你怎么重命名图文件任务都不用改。3. DOT 语法精讲流程图的每种框到底是什么语义3.1 流程图形状与语义对照表画流程图最怕的不是不会连而是框用错。评审的时候被指着说这里明明是判断你怎么画成处理框很尴尬。标准流程图里每个形状都是约定俗成的语义Graphviz 的 shape 属性正好一一对应语义推荐 shape说明开始 / 结束ellipse或box加stylerounded端节点一个流程只有一对处理 / 执行步骤box最常见的矩形写动作判断 / 分支diamond一进多出边上必须带条件输入 / 输出parallelogram用户输入、打印结果数据存储 / 数据库cylinder读写库表子流程 / 调用box加peripheries2双线框表示调用另一流程文档 / 报表note或folder生成的报表、归档文件连接点circle加空 label跨页延续时用外部系统box加styledashed第三方接口手工操作trapezoid需要人工介入的环节这里有个细节diamond默认的上下留白比较大如果判断文字只有四个字整个菱形会显得很空把图撑得很高。解决办法是给它单独设height和width或者用fixedsizefalse配合margin微调。我一般会给判断节点加一句margin0.05,0.02肉眼看着紧凑很多。3.2 节点、连线的样式与文字排版DOT 的三级属性继承机制一定要搞明白这是少写一半代码的关键digraph style_demo { graph [fontnameMicrosoft YaHei, ranksep0.6, nodesep0.4]; node [fontnameMicrosoft YaHei, fontsize12]; edge [fontnameMicrosoft YaHei, fontsize10]; }graph是全局node是所有节点的默认值edge是所有边的默认值。你在声明某个具体节点时再写属性就会覆盖上面的默认值。所以正确的写法是先把 90% 节点共有的属性写在默认里只在个别节点上写差异项。我见过有人每个节点都写一遍 fontname图一改字体名就要改三十处很痛苦。文字排版这块三个转义符必须记住\n换行并居中最常用\l换行并左对齐长说明文字用它好看\r换行并右对齐。比如label校验用户名\n是否重复在菱形里就是两行居中。要注意的是在 DOT 里\n是字面的反斜杠加 n 两个字符不是编程语言里的转义换行写在代码块里就是原样。颜色用十六进制最省事fillcolor#eef4fb配color#4a7ebb是我用了一年的浅蓝组合投屏和打印都不刺眼。stylerounded,filled表示圆角加填充penwidth1.2控制边框粗细。边上的箭头样式由arrowhead决定常用的有normal实心箭头、veeV 形、diamond菱形、odot空心圆判断分支的否回路我习惯用arrowheadvee区分一下。3.3 布局参数用约束去引导算法而不是硬掰前面说了 dot 引擎会自己分层那怎么让它按你的想法排核心就几个参数。rankdir决定分层方向TB从上到下、LR从左到右、BT和RL是反向。流程图默认TB但流程节点很多、每层又很宽的时候改成LR常见效果更好尤其适合页面横向滚动的文档。nodesep和ranksep分别是同层节点间距和层间距单位是英寸默认 0.25 和 0.5。中文节点普遍比英文宽我一般会把nodesep提到 0.4~0.6不然箭头标签会跟相邻节点贴在一起。强制同层用{ ranksame; a; b; c; }。这是画并行分支的利器比如发送邮件和写日志两条并行线强制同层后视觉上就整齐了。constraintfalse是解耦的神器。默认情况下每条边都会影响分层但有些边只是表达顺带调用了某个东西不该影响层级。比如记录日志这一步指向日志表如果这条边参与分层日志表节点会被硬生生压到下一层。加上constraintfalse之后这条边就只管连线不管层。splines控制连线形态spline是默认曲线polyline是折线ortho是正交折线也就是横平竖直。流程图配ortho看着最专业但它有两个限制要提前知道不支持自环而且边上的标签位置可能跑偏。如果用了ortho发现标签乱飞要么改回polyline要么把标签挪到节点内部。不可见边调顺序这是我压箱底的技巧。同一层里几个节点的左右顺序算法是按声明顺序和边关系猜的猜错就乱。想要强制顺序用零宽度的隐形边{ ranksame; a; b; c; } a - b - c [styleinvis, weight10];styleinvis让它不显示weight10告诉算法这条约束很重要优先满足。这招在给流程图排序新增/编辑/删除/查询四个操作节点时特别好用。3.4 集群与分组subgraph cluster 的正确用法当一个流程图里包含用户中心订单中心支付网关这样的模块划分时光靠节点颜色区分不够得用集群把框起来。subgraph cluster_user { label 用户模块; style rounded,filled; fillcolor #f7fafd; color #c3d6ea; fontname Microsoft YaHei; login; register; profile; }三个必须记住的点第一子图的名字必须以cluster开头否则它只是个逻辑分组不会画边框。这个坑几乎每个新手都踩过我当年死磕了半小时才发现。第二集群里的节点和外部的节点连边时层级关系会变得很微妙容易出现集群被拉长、被压扁的情况。当跨集群连线很多时我建议先把跨集群的边减到最少只留主干那一两条。第三如果你想让边连到集群的边框上而不是具体节点需要开启compound并使用lhead/ltaildigraph compound_demo { compound true; subgraph cluster_a { labelA 模块; a1; a2; } subgraph cluster_b { labelB 模块; b1; b2; } a2 - b1 [lheadcluster_b]; }不加compoundtrue的话lhead是不生效的这也是个高频踩坑点。4. 完整实操用户管理模块流程图从零到导出4.1 先列图元清单再动手写代码很多人写 DOT 的习惯是打开文件直接敲敲到一半忘了还差哪个分支回头补的时候发现层级全乱。我的做法是先在纸上或者 VSCode 的空白注释里列清单标清楚每个环节属于什么类型、有几个出口。下面以用户管理模块为例清单是这样的序号环节类型形状出口数1进入用户管理页起点ellipse12权限校验判断diamond23加载用户列表处理 读库box → cylinder14展示列表输出parallelogram15选择操作类型判断diamond46新增 / 编辑 / 删除 / 查询处理box各 17参数合法性校验判断diamond28写库 / 读库存储cylinder19刷新列表处理box110结束终点ellipse-清单列完代码基本就是翻译工作。这样做还有一个额外好处评审时你能直接拿这张表对着图核不会漏环节。4.2 主干流程先写通别一上来就追求好看第一版我会写得很朴素只保证逻辑对digraph user_manage { rankdir TB; start [shapeellipse, label开始]; auth [shapediamond, label有权限]; load [shapebox, label加载用户列表]; show [shapeparallelogram, label展示列表]; choose [shapediamond, label选择操作]; add [shapebox, label新增用户]; edit [shapebox, label编辑用户]; del [shapebox, label删除用户]; query [shapebox, label查询用户]; end [shapeellipse, label结束]; start - auth; auth - load [label是]; auth - end [label否]; load - show; show - choose; choose - add; choose - edit; choose - del; choose - query; add - end; edit - end; del - end; query - end; }跑一遍预览确认四条分支都出来了。这一步不要急着调样式先用最丑的版本验证逻辑闭环因为逻辑改起来涉及增删节点样式改只涉及属性两者混着做容易乱。4.3 补分支、加集群、加数据层逻辑对了之后开始加细节。给四个操作节点补上各自的后续步骤把数据存储抽成独立集群同时把公共样式统一提到默认属性里digraph user_manage_full { rankdir TB; compound true; graph [fontnameMicrosoft YaHei, ranksep0.55, nodesep0.45, bgcolorwhite, pad0.3]; node [fontnameMicrosoft YaHei, fontsize12, penwidth1.2, stylerounded,filled, fillcolor#eef4fb, color#4a7ebb, margin0.18,0.10]; edge [fontnameMicrosoft YaHei, fontsize10, color#6b7a8d, arrowsize0.8]; /* ---------- 端节点 ---------- */ start [shapeellipse, label开始, fillcolor#dff3e3, color#4c9a63]; end [shapeellipse, label结束, fillcolor#dff3e3, color#4c9a63]; /* ---------- 主干 ---------- */ auth [shapediamond, label有权限, fillcolor#fdf3dd, color#c99a3d]; subgraph cluster_core { label 用户管理模块; style rounded,filled; fillcolor #f7fafd; color #c3d6ea; load [label加载用户列表]; show [shapeparallelogram, label展示用户列表]; choose [shapediamond, label选择操作类型, fillcolor#fdf3dd, color#c99a3d]; add [label新增用户]; edit [label编辑用户]; del [label删除用户软删除]; query [label按条件查询]; valid [shapediamond, label参数合法, fillcolor#fdf3dd, color#c99a3d]; tip [shapenote, label提示错误信息, fillcolor#fdeeee, color#c76a6a]; refresh[label刷新列表]; } subgraph cluster_db { label 数据层; style rounded,filled; fillcolor #f6f2fc; color #c7b6e4; user_tbl [shapecylinder, label用户表]; log_tbl [shapecylinder, label操作日志表]; } /* ---------- 连线 ---------- */ start - auth; auth - load [label是]; auth - end [label否\n提示无权限]; load - show; show - choose; { ranksame; add; edit; del; query; } add - edit - del - query [styleinvis, weight10]; choose - add; choose - edit; choose - del; choose - query; add - valid [constraintfalse]; edit - valid [constraintfalse]; del - valid [constraintfalse]; valid - user_tbl [label是, lheadcluster_db]; valid - tip [label否]; tip - refresh; user_tbl - refresh; query - user_tbl [label读取]; refresh - log_tbl [label记日志, styledashed, constraintfalse]; refresh - end; }这段代码里有几个值得单独说的处理。constraintfalse用在 add/edit/del 指向 valid 的边上这三条边如果参与分层会让 valid 节点被压到很下面同时把并列的操作节点拉开。加上之后valid 只是逻辑上连着不参与层级推导图会紧凑很多。ranksame加不可见边四个操作节点强制同层并且用weight10的隐形边锁定左右顺序为新增→编辑→删除→查询跟大多数后台界面的按钮顺序一致。lheadcluster_db让是这条边直接指向数据层集群的边框视觉上表达写入数据层而不是写入某个具体表语义更准确。前提是顶上开了compound true。refresh - log_tbl用styledashed加constraintfalse日志记录是附带的旁路动作虚线加不参与分层一眼就能看出它不是主干。4.4 视觉规范与微调让图能直接进文档代码跑通之后就是调美观这一步我的标准是缩到 50% 还能看清。几条实测有效的经验颜色不要超过四组。我的配色逻辑是端节点绿色、判断节点黄色、处理节点蓝色、异常节点红色、数据层紫色。五组封顶再多就花。同一张图里所有节点用统一的fontsize只有集群标签可以稍大一号。边上的标签尽量短。是否两个字最好超过六个字就考虑挪进节点里或者用\n拆行。判断分支的两个标签最好一左一右自然分开如果都挤在同一侧可以在其中一条边上加xlabel换位置或者调整边的weight影响排序。集群填充色要浅到几乎看不见。集群的作用是分区不是装饰#f7fafd这种接近白色的蓝灰刚好深了会盖住节点。如果图整体偏宽把rankdir从TB换成LR试试往往能压掉一半高度。反过来如果纵向太长就把几个并行的处理步骤用ranksame拉平。4.5 导出格式怎么选DPI 到底该设多少导出格式我按用途分三类进技术文档、要放大不糊用 SVGdot -Tsvg进 PPT、Word、要发给不装工具的人用 PNG注意 DPI要打印、要归档用 PDF矢量且字体处理更稳。命令本身很简单dot -Tsvg user_manage.gv -o user_manage.svg dot -Tpng -Gdpi150 user_manage.gv -o user_manage.png dot -Tpdf user_manage.gv -o user_manage.pdf重点说 DPI。Graphviz 输出 PNG 的默认分辨率是 96 dpi一张布局宽度 6 英寸的图出来只有 576 像素宽放到 Word 里一放大就模糊。换算公式很直白目标像素宽 布局宽度英寸 × DPI假设你的图布局宽度是 6.2 英寸要满足 A4 纸 300 dpi 打印A4 有效打印宽度约 2480 像素需要2480 / 6.2 ≈ 400也就是说-Gdpi400才够。实际工作中不必卡这么死-Gdpi200到300已经能满足绝大多数场景。想直接控制输出尺寸还可以配合-Gsizedot -Tpng -Gsize8,10 -Gdpi200 user_manage.gv -o user_manage_big.png-Gsize后面的!如8,10!表示强制拉伸到指定尺寸、不保持宽高比一般不加更好。还有个小经验SVG 里引用的是字体名而不是字体本身同事机器上没装微软雅黑就会替换成默认字体行距会变。如果这份 SVG 要外发建议用命令行工具把文字转成路径inkscape --export-text-to-path --export-plain-svg user_manage.svg -o user_manage_outlined.svg转完之后任何机器打开都一模一样代价是文字不能再被搜索和选中。5. 踩过的坑常见问题排查速查表5.1 渲染不出来或中文变方块这类问题占了我所有求助的七成。先给一张速查表现象大概率原因解决方式预览空白 / 报spawn dot ENOENT引擎未安装或不在 PATH装引擎重启 VSCode插件设置里路径填错路径带空格或用了反斜杠转义错误用正斜杠如C:/Program Files/Graphviz/bin/dot.exe中文显示为方块fontname用了系统不存在的字体换成系统已装字体部分中文正常部分乱码文件编码不是 UTF-8用 VSCode 另存为 UTF-8 无 BOM集群标签也是方块只设了node的字体没设graph补graph [fontname...]边上的标签中文乱码没设edge的字体补edge [fontname...]字体名要跟系统里的实际名称对得上。Windows 上稳妥的选择是Microsoft YaHei微软雅黑或SimHei黑体macOS 用PingFang SCLinux 服务器上如果没有中文字体先装一套思源黑体之类的开源字体然后用下面这条命令确认准确名称fc-list :langzh | head -20输出里冒号后面那一串就是字体名逗号分隔的别名随便挑一个都能用。注意graph、node、edge三级的 fontname 要分别设置。只设 node 的话集群标签和边标签还是会乱码这三个地方字体来源是独立的。还有一个容易被忽略的点如果你在服务器上跑批量导出服务器没装中文字体出来的图就是方块。这不是 DOT 代码的问题是环境问题别在代码里反复调字体名先去装字体。5.2 图能出来但布局难看节点重叠。最常见的原因是用了splinesortho之后算法为了让线横平竖直把节点挤到了一起。解决办法是加大nodesep或者改回默认的spline曲线。另外 SVG 里节点重叠有时是 HTML 表格标签尺寸算错了比如TABLE忘了设CELLPADDING实际渲染尺寸比预期大跟邻居撞上。连线穿过节点。这是分层布局的天然特性不是 bug。能做的干预有三个调整节点声明顺序、用不可见边改变同层顺序、给关键边加weight5或更高的权重让算法优先拉直它。如果某个节点总被穿考虑把它移到图的一侧用一个ranksame的虚拟节点占位。同一层节点顺序总是不对。前面讲过的隐形边是解法这里补充一个细节隐形边的方向要和真实连线方向一致否则会形成反向约束反而更乱。另外weight给到 10 以上才有明显效果给个 2、3 基本没感觉。集群被拉得又长又扁。原因是集群内的节点和外部节点之间有大量连线每条线都在拉扯集群的边界。解法是减少跨集群的边只保留主干或者把跨集群的边改成用lhead/ltail连到集群边界减少对内部节点位置的影响。图太宽超出页面。三个方向rankdir换方向、把并列的操作节点用ranksame收拢、或者干脆按业务拆成两张图。我个人的原则是一张流程图不要超过 25 个节点超过了就说明这个流程本身该拆了。边上的标签压住别的线。这是 dot 引擎计算标签位置时的常见问题没有完美的解法。可行的绕过方式是用xlabel代替label或者给标签加headlabel/taillabel让它贴在线的两端而不是中间。5.3 工程化问题批量导出、版本管理、CI 集成手上的图一多单张手动导出就不现实了。Linux/macOS 上一行循环搞定for f in *.gv; do dot -Tsvg $f -o ${f%.gv}.svg; doneWindows 的 PowerShell 版本Get-ChildItem *.gv | ForEach-Object { dot -Tsvg $_.FullName -o ($_.BaseName .svg) }版本管理方面我强烈建议把 .gv 源文件提交进仓库把生成的 SVG/PNG 排除掉在.gitignore里加上*.svg、*.png的对应规则。原因很简单图片是产物提交进去每次改动都是整文件替换仓库迅速膨胀而 .gv 是纯文本改动就是几行 diffreview 起来清清楚楚。产物交给 CI 生成即可。如果希望代码风格统一可以了解一个冷门但好用的命令dot -Tcanon input.gv -o output.gv-Tcanon会把图规格化成标准形式输出属性顺序、缩进都统一适合在提交前跑一遍避免不同人写的 DOT 风格差异太大导致 diff 噪音。CI 集成也很简单以常见的持续集成环境为例构建步骤里装好 graphviz然后跑批量导出脚本把生成的 SVG 作为构建产物上传即可。这一步的价值在于文档里的图永远不会过期因为每次构建都是从最新的 .gv 重新生成的。写到这里我想说一个自己的体会。刚用 Graphviz 的前两周我确实觉得写代码画图比拖拽慢尤其是想微调某个节点位置的时候改参数不如拖一下直观。但第三张图开始优势就反过来了——复制上一张图的骨架改掉节点文字和分支条件十分钟出一张新的。现在我的项目里所有流程图、状态图、部署拓扑都放在一个docs/diagrams目录下二十多个 .gv 文件改需求的时候改几行文本按一下CtrlShiftBSVG 全部刷新。如果一开始有人告诉我这套流程我大概能省下当年那些对着箭头逐个对齐的下午。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价