拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Mermaid图工程化指南:从风格漂移到CI渲染规范

开源社区最近流传一个调侃开发者 Dex Horthy 评价由 OpenCode 这类自主开源创作集体产出的 Mermaid 图说渲染风格一眼就能认出来像同一个模板复制的。这个评价虽然带着玩笑意味却点出了 OSS 文档里非常普遍的现象Mermaid 正在成为项目架构、工作流和状态说明的标准图表语言但图的语法表达、主题配色、节点命名和验证方式经常缺少统一约束。与其把这个调侃当成段子不如把它当成一次代码评审意见。围绕这个调侃我会从工程角度拆解四个具体问题Mermaid 的渲染原理是什么为什么同一份图在不同环境里长得不一样本地和 CI 环境如何做到可复现渲染OpenCode 或多人协作场景下Mermaid 语法和风格为什么容易漂移以及怎么用一套轻量规范让 OSS 项目的图表从“贴了一张图”变成“可维护的文档资产”。整篇会围绕最小可运行案例展开适合正在做开源文档规范、或者想解决 Mermaid 图频繁渲染失败的开发者阅读。1. 先理解这个调侃真正戳中的技术痛点1.1 为什么 Mermaid 在 OSS 项目中如此普及Mermaid 是一个基于 JavaScript 的图表 DSL。它允许用文本描述流程图、时序图、类图、状态图、甘特图和饼图等常见图类型。对开源项目来说文本化意味着图可以进 Git可以在 Code Review 时做 diff可以被脚本批量校验也可以直接嵌在 Markdown 文档里。这一点和传统拖拽绘图软件有本质区别。传统绘图工具产出的图片第一次画完很好看但第二次修改时很难发现改动了哪里即使有版本管理diff 出来的也是二进制文件。Mermaid 的源码是一段普通文本任何一个 PR 提交者都能在代码评审页面直接看到结构变化这是它成为 OSS 文档标配的根本原因。但文本化也带来了新的问题。编写 Mermaid 源码本质上等于写一种小型 DSL可是很多开发者并不把它当作程序代码对待。节点命名随心情方向从 LR 到 TD 混用颜色直接硬写到节点上一个多人项目里往往能凑出好几种截然不同的“风格”。Dex Horthy 的调侃如果翻译成技术语言就是这些图的视觉高度趋同说明生成过程缺少项目特有约束一旦约束不存在图的风格、语法健壮性和可维护性自然不会稳定。1.2 把“渲染风格”拆成三层问题要落地解决不能只讨论“好不好看”。从工程角度“渲染风格”可以拆成三层第一层是语法层。Mermaid DSL 对缩进、空格、引号和括号有一定要求。语法错误会导致整张图渲染失败或者局部节点消失这是最容易被调侃的一类问题。第二层是表现层。即使语法完全相同打开 Mermaid Live Editor、本地 CLI 和不同静态站点插件最终 SVG 的字体、配色、箭头形状也可能不一样。表现层的差异由 theme、themeVariables、CSS 和渲染环境共同决定。第三层是协作层。多人或 AI 工具共同产出文档时文件名、目录结构、图类型选择、节点标签语言、颜色约定都要有统一规范。否则即使每一张图单独看都没问题放在同一份文档里也会显得割裂。后面所有章节都会围绕这三层展开。语法层解决能不能渲染的问题表现层解决渲染出来是否符合预期的问题协作层解决长期维护时是否可持续的问题。2. Mermaid 的渲染流程与风格控制点2.1 从 DSL 到 SVG中间发生了什么要理解 Mermaid 为什么容易“渲染失败”先要理解它的工作方式。Mermaid 浏览器端渲染时会经历几个步骤获取带有mermaid类型或mermaid.initialize指定容器的代码块。使用内部解析器分析 DSL 结构识别图类型和实体、关系。将 AST 转换成内部图数据模型。调用对应图类型的渲染器生成 SVG。将 SVG 插入页面并由主题变量决定最终 CSS 样式。这个流程决定了两个重要特性。第一一旦 DSL 解析失败通常会中断渲染只显示一个错误提示而不是“能渲染多少算多少”。这是安全意识设计避免图表被错误部分污染。第二最终显示效果不是直接从 DSL 文本到像素的一一映射中间有解析器和主题系统两层解释所以不同版本、不同配置下渲染结果会不同。2.2 一个最小 Mermaid 流程示例下面是一个最基础的流程图代码块。在支持 Mermaid 的 Markdown 平台里它会被直接渲染成带箭头的图形。flowchart LR A[用户上传配置] -- B{格式校验} B -- 通过 -- C[写入配置中心] B -- 失败 -- D[返回错误信息]这段代码的关键点有三个flowchart LR声明图类型和方向。LR表示从左到右TD表示从上到下。节点A[用户上传配置]的含义是定义节点 ID 为A显示文本为“用户上传配置”。中括号表示普通矩形节点。节点B{格式校验}使用花括号会渲染成菱形通常代表判断分支。B -- 通过 -- C定义了一条从B到C的边并在边上显示文本“通过”。这个最小示例已经覆盖了 Mermaid 的核心概念节点、边、标签、形状和方向。绝大多数协作风格问题都体现在这些基础元素的写法不统一上。2.3 渲染风格由哪些因素决定Mermaid 允许通过init指令或者 CLI 配置指定theme和themeVariables。默认主题包括default、neutral、dark、forest等不同主题会改变节点颜色、边框、线条颜色和背景色。下面这段代码使用了init指令把主题切到neutral同时指定了中文字体族%%{init: {theme: neutral, themeVariables: {fontFamily: Inter, PingFang SC, Microsoft YaHei}}}%% flowchart LR A[用户请求] -- B{权限校验} B -- 通过 -- C[业务处理] B -- 拒绝 -- D[记录日志]这里的fontFamily很重要。默认主题可能使用西文字体中文渲染时如果缺少回退字体在不同操作系统的浏览器里可能显示为不同字体导致同一张图在不同人屏幕上看起来不一样。通过themeVariables指定字体族是把渲染结果统一起来的第一步。2.4 风格漂移主要有四个来源我把常见风格漂移来源整理成了下面的表控制点影响范围常见问题推荐做法flowchart方向整张图布局方向有人用 LR有人用 TD项目约定默认方向一般流程用 LR层级关系用 TD节点 ID 与标签语法正确性和可读性ID 里带空格、特殊字符导致解析失败ID 用英文单词或驼峰展示文本用双引号包裹主题配置全局配色和字体每个节点单独写颜色使用.mermaidrc.yml定义统一主题标签语言文档一致性中英文混用、换行不规范根据文档受众规定展示文本语言和换行规则这四类问题叠加起来就形成了“一眼就能认出是不同人写的”的割裂感。要解决它不能靠口头强调要靠模板、脚本和 CI 一起兜底。3. 搭建一套可复现的 Mermaid 渲染与校验环境3.1 在 Mermaid Live Editor 中快速调试当你第一次接触某个语法或者遇到某张图渲染失败时最快的验证工具是 Mermaid Live Editor。它是官方提供的在线编辑器左侧写 DSL右侧实时渲染可以直接查看 HTML 和 SVG 源码。即使是团队项目我也建议先把它用于“单图快速排查”而不是作为最终管理方式。在线编辑器的优势是反馈快缺点是它无法和 Git 的目录结构、CI 校验联动。因此一个可复现的 OSS 项目还需要在本地把 Mermaid CLI 跑起来。3.2 安装 Mermaid CLI把渲染变成命令Mermaid CLI 是官方维护的命令行工具命令名通常为mmdc。它内部依赖 Headless Chromium 完成 SVG 输出因此安装时可能需要下载浏览器依赖。以一个空目录为例初始化并安装npm init -y npm install -D mermaid-js/mermaid-cli安装成功后可以先创建一个example.mmd文件再把这段 DSL 放进去flowchart LR A[服务启动] -- B{读取配置} B -- 成功 -- C[初始化连接池] B -- 失败 -- D[退出并记录日志]输入以下命令渲染成 SVGnpx mmdc -i example.mmd -o example.svg -b transparent-i是输入文件-o是输出文件-b用来设置背景色。使用transparent背景会更适合嵌入到浅色和深色主题都存在文档站点中。如果你的网络环境安装浏览器依赖比较慢或者 CI 对 Chromium 有额外依赖可以把渲染容器化或者先只做语法静态检查把完整渲染放到发布流水线执行。落地前要确认 npm 包版本和本地 Node 环境不要假设一定使用最新主版本。3.3 用.mermaidrc.yml统一主题和字体单独靠命令参数还不够。为了让所有人在本地渲染出同一套风格项目根目录可以增加一个.mermaidrc.ymltheme: neutral themeVariables: fontFamily: Inter, PingFang SC, Microsoft YaHei primaryColor: #f6f8fa primaryBorderColor: #0969da lineColor: #57606a flowchart: curve: basis htmlLabels: true这个文件的作用是把“好看”和“一致”沉淀成配置。primaryColor是节点主背景色primaryBorderColor是节点边框色lineColor是箭头和线条颜色。把这些变量放到统一配置后任何节点都不应该再单独写style颜色因为一旦有人写了后续维护基本会失控。3.4 把 Mermaid 文档接入 Markdown 和静态站点Mermaid 源码可以出现在仓库的docs/diagrams目录里也可以直接写在 Markdown 代码块中。两者的使用场景不同。在 GitHub 上你可以在 Markdown 中直接写mermaid flowchart LR A[用户] -- B[服务]这种写法的优点是简单缺点是图表源码和 Markdown 强耦合。如果某张图被多个文档复用你就要反复复制同一份源码。 更推荐的做法是维护独立 .mmd 文件渲染成 SVG 后再供 Markdown 引用。例如把源文件放在 docs/diagrams把产物放在 docs/images文档只维护相对路径 markdown ![登录流程](./images/user-login-flow.svg)这样做的好处是CI 可以遍历docs/diagrams里的所有.mmd文件统一渲染并检查渲染结果是否和仓库中已有的 SVG 一致。只要有人改了图但忘了更新 SVG流水线就会暴露问题。4. 排查思路从“渲染失败”倒推到“风格漂移”4.1 先把问题定位到“语法层”还是“样式层”遇到 Mermaid 图效果不对时不要第一时间改样式。先判断问题出在哪一层。如果在 Mermaid Live Editor 或页面里出现Syntax error、Rendering Error、Unknown diagram type说明第一步是语法或解析层问题。你需要缩小代码范围。最简单的方法是把图拆分到只剩一行节点和一条边确认这段核心结构能渲染再逐步加回节点和分支。如果图能渲染但字体不对、颜色不统一或者在不同环境下不一样问题就在样式层和配置层。此时去检查.mermaidrc.yml是否存在、CLI 是否真的读取了它、字体族是否包含中文字体回退。不要在一个节点上反复试颜色因为那只是掩盖问题。下面的表格总结了几个高频问题问题现象可能原因检查方式处理建议整张图渲染失败某个节点或边的文本包含未转义特殊字符在 Live Editor 中逐步删除节点缩小范围给文本加双引号特殊字符优先转义本地正常但 CI 渲染不同环境字体或主题配置未统一查看 CI 日志中读取的配置文件在仓库根目录统一.mermaidrc.yml中文显示为方块SVG 字体缺失检查生成的 SVG 的font-family配置fontFamily为系统常见中文字体多人提交后风格混乱缺少静态检查搜索style和节点颜色硬编码引入脚手架脚本和 PR 检查改了.mmd但文档图片没变没有自动重新渲染查看 Git 差异增加 CI 渲染并校验产物4.2 三个最典型的语法坑第一个坑是节点文本包含冒号、括号和花括号等符号却没有加双引号。Mermaid 在很多实现里会把[和]作为节点边界边界内的某些符号需要特殊处理。稳妥做法是最外层使用双引号包裹展示文本flowchart LR A[Token: 过期时间(60s)] -- B[刷新会话]第二个坑是边的文本里再次出现双引号。例如想表达“用户点击确认按钮”时如果周围没有规范限制代码很容易写成flowchart LR A[用户点击确认按钮] -- B[提交订单]这里的关键是让文本保持简洁不要试图把一句话塞进边里。边上只放简短动词或状态词长句放到节点正文里能大幅降低转义压力。第三个坑是图的方向和类型混用。有人把sequenceDiagram当成流程图来写有人把flowchart和graph混用还有人把TD和LR换成TB、BT。方向和类型的组合虽然多但团队里只需要固定一到两种。例如业务流程统一用flowchart LR有严格层级或嵌套关系的统一用flowchart TD。4.3 怎么查“风格漂移”而不是只查“渲染错误”风格漂移通常不会让渲染失败但会让文档专业度下降。最直接的检查方式是用文本搜索grep -R style docs/diagrams || echo 没有发现节点颜色硬编码 grep -R %%{init docs/diagrams如果仓库里大量出现style A fill:#xxx说明主题变量没有真正生效。这类代码在单张图上可能没毛病但一旦项目换了整体品牌色你就要逐张修改维护成本极高。正确的做法是移除节点级颜色把色值统一放到.mermaidrc.yml的themeVariables中。此外PR 评审时不要只看渲染后的 PNG 截图。要同时看.mmd源码因为截图可以美化源码才是长期保存的资产。如果一个 PR 只提交了.svg或图片没有提交.mmd后面修改就没有可 diff 的基线。5. 给 OSS 创作集体设计一套可复用的 Mermaid 规范5.1 先定义“什么时候用哪种图”很多人是看到某个模板图好看就把它复制进项目。这种复制经常导致时序图里塞流程逻辑甘特图里描述调用关系。更合理的方式是先定义图表类型要表达的内容推荐图类型示例场景业务流程和分支flowchart登录、订单状态流转、网关路由消息交互过程sequenceDiagram鉴权、支付回调、异步通知状态迁移stateDiagram-v2订单状态机、任务生命周期类与接口结构classDiagram领域模型预览数据库实体关系erDiagram核心表设计资源计划和排期gantt发布计划、迭代排期这一条规范的目的是减少“表达方式和图类型不匹配”的问题。如果只是两个服务之间的调用用flowchart会比sequenceDiagram更适合阅读如果要强调消息顺序和谁先发谁后回则应该使用sequenceDiagram。5.2 文件、节点和标签命名约定一个比较稳妥的文件结构如下docs/ diagrams/ user-login-flow.mmd auth-timeout-sequence.mmd images/ user-login-flow.svg auth-timeout-sequence.svg.mmd文件名统一使用kebab-case避免使用空格和中文文件名。文件名就是图表主题后面加.mmd一图一文件。节点 ID 统一使用英文驼峰或下划线。不要把展示文案写进 ID 里例如flowchart LR user[用户] auth[认证服务] db[用户表] user -- auth auth -- db这里user是 ID“用户”是显示文本。展示文本是否加双引号取决于你如何设置团队规范如果图里可能出现中文标点或括号建议统一加双引号。展示文本的语言也要统一。对中文 OSS 团队正文可以用中文但边界状态值建议统一为接口实际返回的英文值让开发和测试都能看懂。标签不要写长句通常控制在六个字以内。5.3 一个可以直接复制改用的模板下面这段模板组合了子图、节点形状、分支和边标签。你可以把它作为团队 PR 的起始模板%% 用途描述从客户端到服务端的简化登录流程 %% 说明节点 ID 使用英文展示文本使用双引号包裹 flowchart LR subgraph Client[客户端] user[用户] end subgraph Server[服务端] login[认证接口] check{令牌校验} pass[签发会话] fail[返回错误] end user -- login login -- check check -- valid -- pass check -- invalid -- fail这里使用了subgraph Client[客户端]。子图的作用不是单纯装饰而是把职责边界在视觉上表达出来。Client是子图 ID“客户端”是子图显示名称。使用子图时要注意缩进子图内节点必须缩进否则解析器可能无法正确分组。如果发现某张图超过 30 条边或者子图超过 5 个说明它不再适合作为一张 Mermaid 图。这时应该拆分每张图只讲一件事复杂链路拆成高层总览图和细节子图。5.4 用脚本做静态风格检查人很难在 PR 评审时注意到所有缩进问题所以静态检查脚本是有价值的。下面是一个 Node 脚本示例它会检查最基础的几个约定// scripts/check-mermaid.mjs import fs from node:fs; import path from node:path; const diagramDir docs/diagrams; const files fs.readdirSync(diagramDir).filter((f) f.endsWith(.mmd)); const errors []; for (const file of files) { const fullPath path.join(diagramDir, file); const content fs.readFileSync(fullPath, utf8); if (!/^(flowchart|sequenceDiagram|classDiagram|stateDiagram-v2|erDiagram|gantt)/m.test(content)) { errors.push(${file}: 首行应声明图表类型); } const edgeCount content.split(\n).filter((line) line.includes(--)).length; if (edgeCount 30) { errors.push(${file}: 边数量超过 30建议拆分为多张图); } if (/style\s\w\sfill:/i.test(content)) { errors.push(${file}: 检测到节点颜色硬编码建议使用 themeVariables); } } if (errors.length 0) { console.error(errors.join(\n)); process.exit(1); } console.log(Mermaid style check passed.);这个脚本只做“约定级检查”不做完整语法解析。完整语法解析应该交给mmdc。你可以把它加入package.json的 scripts 中{ scripts: { diagram:check: node scripts/check-mermaid.mjs } }运行后如果目录里有违规文件会提示具体文件名和原因。脚本本身很短适合作为团队协作的基础版本后续可以按实际需要扩展。5.5 用 CI 阻止不规范的图被合并静态检查脚本只能识别结构问题完整渲染还需要真实执行 Mermaid。可以把两者组合成 GitHub Actions 工作流name: mermaid-check on: pull_request: paths: - docs/diagrams/** - package.json - package-lock.json jobs: check: runs-on: ubuntu-latest steps: - name: Check out repository uses: actions/checkoutv4 - name: Set up Node uses: actions/setup-nodev4 with: node-version: 20 - name: Install dependencies run: npm ci - name: Run style check run: npm run diagram:check这个工作流的主要目的不是替所有人决定图好不好看而是确保每张图至少满足三个基本条件有合法图类型声明没有明显的颜色硬编码整体复杂度没有超过团队阈值。如果你的 CI 环境在运行mmdc时仍然缺少系统字体或浏览器依赖需要单独增加依赖安装步骤或用 Docker 镜像作为运行环境。6. 最佳实践让 Mermaid 图成为协作资产而不是一次性贴图6.1 不要把“能显示”当作完成标准很多开发者在本地看到图能渲染就觉得任务完成了。实际上能渲染只说明语法正确不代表图一定能被项目其他成员理解。后端同学关心流程对不对前端同学关心分支是不是少了异常状况运维同学关心超时和重试是否画进去了。因此在 Code Review 时应该把一个 Mermaid 文件当成一个高信号模块看文本结构是否清晰方向是否统一标签是否准确数据库图有没有冗余关系。用这种方式评审能在合并前发现很多领域问题而不是等文档发布后再被下游使用者吐槽。6.2 让.mmd源文件和 SVG 产物进入版本控制.mmd源文件是必须进入版本控制的因为它是修改的基线。SVG 产物是否进入版本控制取决于团队维护方式。如果文档站点是静态构建的SVG 可以不进仓库由 CI 构建时生成如果团队希望 PR 页面直接看到图片差异SVG 可以进仓库并配合 CI 生成后校验。我比较推荐在关键文档项目中让 SVG 进入仓库。原因是很多开源项目的文档站点并不会有完整构建流水线放一个现成的 SVG 能让贡献者直接修改.mmd后重新渲染提交两个文件的 diff审阅者一眼就能看出图发生了什么变化。6.3 颜色和字体用变量控制不要逐节点写死下面这种写法短期看起来方便长期会带来维护负担flowchart LR A[服务] -- B[数据库] style A fill:#f00,color:#fff style B fill:#0f0,color:#fff如果后续品牌色从红色改成蓝色你需要逐张图搜索style。更合理的做法是配置.mermaidrc.ymltheme: neutral themeVariables: primaryColor: #ffffff primaryTextColor: #24292f primaryBorderColor: #0969da lineColor: #57606a fontFamily: Inter, PingFang SC, Microsoft YaHei颜色和字体一旦放入全局配置图就只负责表达关系不负责表达视觉主题。视觉主题交给统一的渲染配置这样团队里任何一个人渲染出来的图视觉效果都一致。6.4 提交前检查清单每次提交.mmd文件前建议按这个清单快速自查文件路径是否放在docs/diagrams命名是否使用kebab-case。文件头部是否声明了图类型和方向方向是否符合本次表达意图。节点 ID 是否只使用英文字母、数字和下划线展示文本是否统一加双引号。是否还有style节点颜色硬编码。是否存在超过 30 条边或者 5 个以上子图的超大图。是否有对应的.svg产物已经更新或确保 CI 会重新生成。是否在本地用mmdc完整渲染过一次。图里的所有标签是否使用团队约定的语言是否出现长句。分支标签是接口实际状态值还是自定义中文说明。子图分组是否代表了真实的系统边界或职责边界。这个清单可以打印出来贴在团队 Wiki也可以写到.github/PULL_REQUEST_TEMPLATE.md。对新手贡献者来说它比“请保持图表风格一致”这样模糊的反馈有用得多。6.5 下一步可以继续扩展的方向如果团队对 Mermaid 的使用频率很高可以继续做三件事。第一把.mmd目录和文档站点生成流程打通。在每次发布文档时自动渲染所有图并把渲染产物打包上传避免手工提交 SVG 时遗漏。第二尝试把 Mermaid 解析器接入到代码评审机器人。机器人读取 PR 修改的.mmd文件如果检测到语法错误或风格违规直接评论到 PR 下面而不是等人工评审再指出来。第三针对自己的项目沉淀一个“图编写规范”页面。这个页面不需要很长只要说明图类型选择、文件命名、节点标签、颜色变量和复杂度上限。把规范写下来是让风格一致的最基础动作。Mermaid 本身是一种表达工具不是文档项目的目的。真正有价值的不是哪张图渲染得惊艳而是团队能稳定地把复杂关系讲清楚并且每张图在三个月后还能被修改。要把这一点变成现实靠的不是某个人的画图技巧而是一套可以自动执行的约定。Dex Horthy 的调侃其实是个提醒当所有人都用同一种模板生成图时说明工具的便利性已经超越了约束而成熟的 OSS 项目里约束应该跑在工具之前。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门