☰
diagram-design:用文本化定义与样式Token打造标准化的文档图表体系
2026/10/13 1:25:24 网站建设 项目流程

diagram-design 这个名字,乍一听挺像一个画图小工具的代号,实际上我把它当成一个体系来做——它负责的是团队文档里所有图表的定义、生成与视觉规范。起因特别直接:我发现自己团队里架构图、时序图、流程图,格式五花八门,有人用白板导出,有人用幻灯片硬画,同一个服务链路能画出三种箭头方向,配色更是谁新开页面谁说了算。图表本来是辅助沟通的,结果每张图还要额外花时间解释“这张图想说啥”。

diagram-design 要做的事,就是把图表的生成过程标准化:用文本定义结构、用统一布局算法摆放节点、用一套样式 Token 控制视觉表现,最后输出清晰一致的图,并接入文档构建流程。全文没有玄乎的概念,全是可落地的工程细节。适合三类人:被文档评审里“图又画错了”折腾到崩溃的技术负责人,正在做可视化组件或设计系统的工程师,以及任何想让技术文档更规范、更好维护的团队。

1. 为什么需要 diagram-design:从“会画图”到“画对图”

1.1 图表混乱的三个根源:结构、样式与表达

我之前一直以为图表乱是团队美术功底不行,后来才意识到问题没出在“画”上,而是出在“想”上。一张图要成立,至少要过三关。

第一关是结构关。画图的人对系统的理解不一致,节点粒度完全不同。有人按服务画,有人按接口画,有人把数据库也画成一个大方框,根本没标清楚到底画到哪一层。第二关是视觉关。完全没有规范,线宽、颜色、字体、圆角各凭喜好,同样一个“调用关系”,有人用实线,有人用虚线,有人还加粗描边,看起来信息丰富,实际上读者更不知道重点在哪。第三关是表达关。很多图脱离语境,没有图例、没有入口标注、没有边界框,读者根本不知道从哪个节点开始读起。

这三关叠加起来的后果就是文档里的图不仅没能降低沟通成本,反而成了新的认知负担。团队里流传着一句玩笑话:新同学第一次看架构图,光弄懂图例就花了半小时。diagram-design 把这三关分别对应成结构定义、样式 Token 和生成管线,让每张图都天然满足这三层要求,而不是靠画图的人自觉。

1.2 项目边界:先想清楚不做什么,比想清楚做什么更重要

任何一个项目,最难的都是画边界。diagram-design 在一开始就明确了两条红线:不做实时协作、不做自由拖拽画布。市面上成熟的白板产品已经把这些体验做得很好,我们不需要再造一个。

那 diagram-design 做的是什么?它是一条“面向文档的图表生成管线”。输入是文本形式的图定义,经过解析、布局、渲染三步,输出稳定的 SVG/PNG。它不服务于“想到哪画到哪”的头脑风暴场景,而服务于“这张图要进 Wiki、要进评审、要随版本更新”的正式文档场景。

这个边界划分很关键。实时协作图讲究的是人和人之间的互动感,文档图讲究的是可复现、可审查、可版本化。如果试图在一个工具里同时满足这两种需求,往往会两头都做不好。我宁可把交互编辑做得弱一点,也要保证同一个输入在任何时候渲染出来的图完全一致。这个“确定性”正是文档场景里最重要的特性,也是后面所有技术方案选型时反复回归的锚点。

2. 核心设计思路:文本化定义、统一数据模型与样式 Token

2.1 为什么我弃用拖拽画布,改用文本化定义图表

很多人听到“文本定义图”第一反应是反人类:我图都画不明白了,你还让我写代码?但实际用下来,真正常用的场景里,文本化优势是碾压级的。

首先是版本化。图定义和代码一样进版本控制,每次改动都有 diff,评审时能精准看到“谁把箭头方向改了”“谁加了一个节点”。拖拽画布做不到这一点,导出的图片只能整张替换,你根本说不清这版和上一版差在哪。尤其是技术评审时,一个清晰的 diff 比画得漂亮重要得多。

其次是复用。同一份节点定义可以被多张图引用,比如服务清单可以在架构图、链路图、部署图里复用,改一次全图生效。拖拽工具里复制粘贴,改一处忘了另一处,等发现的时候图上已经是历史残留了。

还有可编程生成。diagram-design 的 DSL 本身是文本,所以可以从接口列表、监控元数据、数据库表结构里自动生成图定义。比如新增一个微服务,CI 里跑一个脚本就把依赖关系图刷新了,这种能力拖拽画布永远给不了。

文本化不是目的,可追溯、可复用、可生成才是目的。简单对照一下两种方案:

对比维度拖拽画布工具文本化定义
版本化导出图片,整张替换文本进仓库,逐行 diff
复用性复制粘贴,容易分叉节点定义可被多图引用
可编程几乎不可能可从接口、配置自动生成
稳定性手动微调,千人前面同一输入同一输出
学习成本低稍高,但一两天能上手

2.2 图表的统一数据模型:节点、关系、泳道与分组

数据模型是整套 DSL 的地基。diagram-design 第一版只定义了四种核心原语:节点(node)、关系(edge)、分组(group)、泳道(lane)。看起来少,但这四种原语几乎覆盖了架构图、流程图、时序图、状态机的九成场景。

节点是最小信息单元,表示一个服务、一个组件、一条消息;关系描述节点之间的有向或无向边,支持“同步调用”“异步消息”“数据流”这类语义;分组用于表达模块边界或部署区域;泳道则表达时序上的责任主体。这四种原语组合起来,能表达“在这个模块内,A 调用 B,发生在服务端泳道”这种复杂信息。

数据模型设计里最重要的一条原则是:结构信息和表达信息分离。DSL 里只写结构,比如“service-a 调用了 service-b”,不写“用 2px 红色虚线表示”。颜色、线宽、形状都靠节点类型和关系类型去映射样式 Token。这样做的好处是换主题、换排版时,图定义一行都不用改,结构信息始终是唯一的来源。

这个设计也带来了另一个隐藏收益:图定义可以互相引用。比如把公共依赖提取成单独的片段文件,多个图里 import 同一份定义,既省维护成本,又不会出现同一节点在不同图里叫法不一致的情况。

2.3 样式 Token:让所有图共享同一套视觉语言

视觉问题表面上是不好管理,本质上是缺少抽象。diagram-design 把视觉表达抽象成一套 Token,包含颜色、字体、字号、间距、圆角、线宽、箭头尺寸这几类。每一类都分基础值和语义值两层。

基础值就是真实数值,比如 primary 是某个色值;语义值则描述“这个 Token 是用在什么场景的”,比如调用边、依赖边、数据流边分别对应什么颜色。图定义只引用语义值,不直接写基础值。这样换主题的时候,只需要换一套基础值映射,全团队的图就全部改头换面。

我曾经踩过坑,早期版本里直接在 DSL 里写颜色值,结果想切换深色模式的时候发现所有图定义都得改一遍,后来花了半天重构才把颜色全部抽成 Token。Token 还有一个容易被忽略的作用:可访问性。同一套语义色,要保证在浅色和深色背景下都有足够的对比度,这在视觉验收时是一条硬指标。把 Token 体系建好,等于从系统层面保证了所有图的最低可用标准。

3. 技术选型与核心实现:解析、布局、渲染三步走

3.1 解析器实现:从 DSL 语法到中间表示的工程细节

diagram-design 的执行链路第一站是解析器。解析器做的事可以拆成三个阶段:词法分析、语法分析、语义检查。

词法分析先把 DSL 文本拆成一个一个 token,比如关键字、标识符、字符串、括号;语法分析再按文法把这些 token 组装成语法树;语义检查则负责查错,比如节点是否重复定义、关系里引用的节点是否存在、分组是不是嵌套到了不合理的层级。

语义检查这里我想多说一句。第一版我只做了语法检查,结果用户写错一个节点名,直到渲染出来才发现图里少了一个节点。后来我把“悬空引用”和“重复定义”提升为阻断错误,解析器直接报错并定位到第几行。这个改动让错误暴露时间从渲染后提前到提交前,文档质量立刻上了一个台阶。

作为示例,最基本的图定义长这样:

# diagram-design 示例:简化版订单系统 name: order-system-simple nodes: - id: api type: service label: 订单API - id: core type: service label: 订单核心 - id: db type: storage label: 订单主库 group: data edges: - from: api to: core label: gRPC kind: sync-call - from: core to: db label: SQL kind:>{ "tokens": { "color": { "primary": "#2F6FED", "border": "#CBD5E1", "nodeFill": "#F8FAFC", "callEdge": "#475569", "dataEdge": "#0F766E" }, "font": { "family": "Inter, PingFang SC, Microsoft YaHei, sans-serif", "size": 14, "headingSize": 20, "codeSize": 12 }, "spacing": { "grid": 8, "nodePaddingX": 16, "nodePaddingY": 12, "groupPadding": 24 }, "radius": { "node": 6, "group": 12 }, "stroke": { "width": 1.5, "arrowSize": 10 } }, "semantic": { "sync-call": { "color": "callEdge", "style": "solid" }, "async-msg": { "color": "callEdge", "style": "dashed" }, "data-flow": { "color": "dataEdge", "style": "solid" } } }

这段配置里最值得留意的是 semantic 块。图定义里写的是 kind: sync-call,主题里才能查到一个具体的颜色和线型。如果你想为某个客户做品牌定制,只要复制一份 tokens 改基础色,语义名保持不动就行了。

Token 的值也要讲究来源。网格间距 8 像素来自设计系统里最常用节奏;节点圆角 6 很小,是为了让标签不拥挤,同时保持专业感;线宽 1.5 既保证打印和投影不糊,又不会显得笨重。这些数值都可以根据自己团队的审美调整,关键是全图只能有一套值。

4.3 接入 CI:每次提交代码,文档图自动刷新

图表定义放进仓库只是第一步,真正的价值在于让图和代码同步更新。diagram-design 的 CI 流程我建议做成四步:校验、渲染、对比、提交。

校验阶段跑语义检查,错误直接阻断流水线;渲染阶段把 diagrams 目录下所有图定义产出 SVG 和 PNG,输出到文档资产目录;对比阶段查看这次变更是否产生了非预期差异,比如你只改了一个服务的名字,结果整张图布局全变,这种“意外变更”会在评审里暴露出来;提交阶段把产物提交回仓库,供文档站点自动拾取。

# 本地渲染命令(示意) diagram-design lint ./diagrams diagram-design render ./diagrams --output ./docs/assets --theme dark

这两条命令在本地开发时可以随时跑,CI 里也只是同样的两条。为了不让“渲染”这步无限发散,我强烈建议锁版本安装依赖,不要用浮动版本。曾经有人因为依赖升级导致 SVG 兼容性变化,整批文档图在线上变红框,排查了半天。

CI 里还有一个容易忽略的校验点:SVG 的有效性。有些文档系统对 SVG 解析非常严格,渲染器必须保证输出的是合法 XML。我们早期在一个标题标签里写了未转义的符号,结果整页文档直接挂掉。后来加了一个最小化校验步骤,解析所有 SVG 的 XML 结构,彻底杜绝了这类问题。

5. 常见问题与排查技巧实录:这些坑我替你踩过了

5.1 中文乱码、文字溢出与字体回退

中文是我在开发环境里最先遇到的坑。现象有三种典型表现:渲染出来中文变成方框;节点文字超出背景框一大截;同一个节点在本地和 CI 渲染宽度不一样。

方框大概率是运行环境缺中文字体。解决方案不是简单加一个 font-family,而是让渲染器显式加载一份包含中文的字体文件,并且开发、测试、CI 三套环境用同一个文件,否则环境之间的字体度量不同会导致文本测量结果漂移。

文字溢出则通常是测量函数和渲染函数用的字体不一致。文本测量如果连字号、字重都没对齐,算出来必然偏。建议把测量字体和渲染字体放进同一个配置对象,只允许从那一处读取。超长文本的方案是设置最大宽度后自动换行,或者截断加省略号,同时利用 SVG 的 title 属性让读者悬停看到完整文本,这个交互成本很低但特别实用。

调试技巧:我专门留了一个 debug 主题,把节点填充禁用、边框改成高对比荧光色,跑完一看,哪个节点溢出、哪个框撞了,一眼全暴露。

5.2 箭头重叠、节点碰撞与布局漂移

箭头重叠这个问题,稍微复杂一点的图几乎都会遇到。原因通常不是箭头画错了,而是边的路由没有考虑“别的边”和“节点占位”。分层布局时,如果两条边从同一节点连到相邻层,默认路由会把它们贴在一条线上,肉眼看起来就像双线合并。

我的做法是先按边的语义给一个优先级,比如主干链路优先级最高,先路由主干,再路由旁支,并给每条边一个固定偏移步进。比如横向布局时,同一轨道上的边按 8 像素递增偏移,这样即使平行边也能清晰分开。节点碰撞则多半是布局间距没吃透,网格对齐只处理了坐标取整,节点最小间距还要单独设置,建议至少留出两倍网格的空间。

布局漂移是文档场景最隐蔽的敌人:输入文件一个字节没改,升级一次渲染库,整张图位置全部重排。解决办法核心就是确定性,所有排序都有稳定兜底,所有随机都有固定种子,所有算法都不依赖哈希迭代顺序。把这些落到代码评审要求里,比事后排查有效得多。

5.3 SVG 文件过大导致文档卡顿,压缩手段要分级上

文档站点吃 SVG 卡顿是很常见的,原因也很好猜——图太大、节点太多、代码太啰嗦。曾经有个 400 节点的架构图,生成出来 4.2MB,页面加载直接白屏预警。

最立竿见影的优化有三招。第一招是路径简化,节点和边框能用直线矩形就不要用曲线,一个矩形输出成矩形标签比输出成十段路径体积小一个数量级。第二招是背景网格不要逐格画,用一个 pattern 元素声明一次,让浏览器自己去平铺。第三招是懒加载加按需渲染,文档页面里默认先渲染首屏能看到的节点,滚动到某个区域再补渲染其他部分。

如果图实在大到没法控制,我建议分区域渲染而不是继续压单文件。一张 400 节点的图拆成四个子系统图,每张图单独渲染,再在文档里用链接互相引用,阅读体验和加载性能都远比一张巨图好。控制全局输出体积还有一个方法:默认导出 SVG,按需导出 PNG。PNG 适合嵌入没有矢量需求的场合,体积有时候反而比超大 SVG 更可控。

5.4 排查速查表:问题、现象与对策

问题典型现象排查思路对策要点
中文乱码文字变方框检查运行环境字体显式加载字体文件,三环境统一
文字溢出节点标签超出背景检查测量字体是否一致统一度量配置,超长截断加 title
箭头重叠平行边贴在一起检查边路由偏移语义优先级 + 固定偏移步进
节点碰撞两个矩形交叉检查最小间距间距至少两倍网格,布局后校验
布局漂移同输入位置全变检查算法确定性随机种子、稳定排序、固定兜底
SVG 过大文档卡顿检查路径冗余简化为矩形标签、精简路径、pattern 网格
XML 非法文档整页挂掉检查特殊字符转义渲染后校验 XML 合法性
错误图上线语义检查漏掉检查 CI 阻断规则悬空引用、重复定义阻断流水线

这张表是我自己在维护阶段最常用的速查清单,遇到问题先按表里锁定位,准确率很高。有些问题反复出现,是因为只在文档站里看到最终产物,没有在渲染阶段把错误暴露出来。定位永远要往回走一步,看到 DSL 文本那个层面才能看到真正的病根。

6. 影响范围与落地经验:diagram-design 怎样改变团队工作流

6.1 从“画图评审”到“结构评审”的协作方式变化

diagram-design 上线一段时间后,最明显的变化发生在设计评审会上。以前评审技术方案,大家对着演示文稿里的图争论半天,但每次争论的不是业务逻辑而是图的画法。现在评审直接看提交记录里的图定义变更,每一行变更对应一个具体结构变化,讨论焦点自然从“箭头为什么是这个方向”变成“这条依赖是不是真的存在”。

另一个变化是文档的图不再过期。以前架构图三个月没人维护,节点和线上系统完全对不上。现在图定义与代码同仓,服务变更时必须同步改图,否则 CI 里有个检查不通过。把维护图变成开发流程的一部分,而不是文档的额外负担,这才是长期有效的机制。

新同学的学习路径也被缩短了。以前要先学会某个绘图工具,还要理解团队里约定俗成的视觉习惯;现在直接读图定义文件就能看出系统的边界和依赖关系,比读一段文字描述直观得多。有个同学说,自己还没看文档正文,先把几张图定义过了一遍,对系统的理解已经超过了看旧文档一星期的效果。

6.2 适用边界与后续能扩展的方向

diagram-design 不是万能的。它适合表达有具体结构关系的图:架构图、链路图、时序图、流程图、简单状态机、数据流图。这些图的共同特点是结构占据核心地位,视觉服务于结构。但如果你要画的是营销海报、手绘风格示意图,或者需要复杂数据可视化的分析大屏,这套体系反而不合适,那些场景里视觉表达才是主角,直接用专业设计工具更合适。

后续扩展我比较看好的方向有三个。一是增量渲染,图局部变化时只重排受影响的子图,而不是全图重排,能显著降低大型图的维护成本。二是交互高亮,文档里的图与正文联动,鼠标悬停节点时正文对应段落高亮,这在复杂架构文档里很实用。三是多主题同步发布,比如同一张架构图自动输出亮色、暗色、品牌定制三套版本,文档站点根据用户偏好自动切换。

整个链路跑了一年多,我最大的感受是:做这类工具,重点不在于渲染效果多炫酷,而在于把“设计规范”从一种口头约定变成一套可执行的系统。当团队开始用同一套语言讨论图表的每一层时,沟通成本降下来的幅度远超预期。

最后分享一点私心体会。做 diagram-design 的过程中,我最大的冲动是加拖拽缩放、逻辑编排、实时连线这类交互功能,后来全部砍掉了。文档里的图和编辑器里的图,天生是两种物种,前者要的是稳定与可追溯,后者要的是自由与手感。真正让团队产生变化的,反而是最不起眼的样式 Token 和那条“图必须随代码提交”的自动化规则。一个小技巧送给想复刻的人:渲染命令务必留一个 debug 参数,内部切到高对比度调试主题,把分组边界和节点层级用强色标出来,审查复杂图时看得清清楚楚,比任何巡检脚本都直观。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询