简介:Mermaid.js v10.6.1 压缩版是一份面向 Web 前端开发者和文档编写者的流程图可视化工具,尤其适合在内网或离线环境中快速构建专业图表。它以简洁的文本语法为核心,只需编写少量标记代码,就能自动生成流程图、序列图、甘特图、类图等常见图表,省去了手动拖拽绘图和复杂样式调整的麻烦。v10.6.1 在稳定性、渲染速度与已知问题修复上均有改进,整个压缩包仅含 1 个 JS 文件,包体约 851KB,独立文件形式可直接在前端项目中引用,无需连接外部 CDN 或额外依赖,适合嵌入网页、应用、文档与报告。该版本同时支持与 VS Code、Jira、GitLab 等主流工具链协作,方便开发者在日常流程中快速制作和维护图表。目前已有 504 人学习/下载,无论用于快速原型设计、功能展示还是数据分析,都能获得轻量且专业的可视化方案。
1. 为什么用 JavaScript-Mermaid 渲染流程可视化:从 Markdown 到图的最后一公里
做技术文档的人大概都有这种经历:架构图、流程图、时序图画了一遍又一遍,改一个节点就得重排整张图,图一多就没人维护,最后文档里的图和代码里的逻辑完全对不上。我后来把整套流程图的产出方式换成了 Mermaid 这套基于 JavaScript 的渲染工具,具体到这个标题里的v10.6.1.min.js,它就是从文本到 SVG 流程图之间的那台“翻译机”。你不用再对着画布拖拽框线,只需要用文本把节点和连线描述出来,渲染工具负责排版和出图。
这个方法最直接的收益是流程图可以被 Git 管理、被 Code Review、被自动化测试覆盖。后端同事改接口、前端同事改页面,顺手改一段 Mermaid 文本就能让文档保持新鲜。适合的人也很明确:写技术方案的人、维护接口文档的人、以及想在自己项目里动态生成流程图的 Web 前端开发者。本文就从渲染工具的加载原理讲起,逐步落到最小可复现工程、定制交互和生产环境避坑。
2. v10.6.1.min.js 的渲染原理与选型理由:为什么文本比画布更可靠
2.1 从手绘画布到文本建图:流程图可视化工具的分水岭
主流流程图可视化方案大致能分成三条路线。第一条是 PlantUML 那类,通过 Java 后端生成图片,胜在时序图、用例图支持比较全,缺点是要装 Java 环境,在浏览器端做动态渲染比较笨重。第二条是 Excalidraw、draw.io 这类画布工具,交互体验好,适合脑暴和快速示意,但图一旦进画布就成了孤岛,难以版本化,也无法被程序按需修改。第三条就是 Mermaid 这类文本即图表的工具,语法简单,天然嵌入 Markdown 生态,还能在前端运行时直接渲染成 SVG。
Mermaid 能覆盖的范围不止流程图,它还支持时序图、甘特图、类图和 ER 图。也就是说,如果你需要把业务表结构的关系画出来,用 Mermaid ER 图比手画快得多;想把会议思路整理成分支结构,Mermaid 思维导图也能胜任。这也是我把项目里的架构文档全部迁到 Mermaid 上的原因——一套语法覆盖多类图,不用在多种工具之间来回切换。
选型时我还会看生态。Mermaid 在 VS Code、Obsidian、Typora 这些主流编辑器和笔记工具里都有预览支持,几乎成了 Markdown 图表的默认选项。v10.6.1.min.js是 v10 系列压缩后的浏览器端运行时,单文件、体积可控,不依赖框架,任何传统页面都能直接引入,这也是它能被大量文档工具内置的原因。
2.2 v10.6.1 的加载机制:从 grammar 解析到 SVG 注入
Mermaid 的渲染链路可以拆成四段:词法解析、语法解析、布局计算和 SVG 输出。先说最容易被误会的部分:Mermaid 不是“根据模板拼字符串”,它确实有一套自己的 BNF 文法。v10.6.1.min.js内嵌了 jison 生成的解析器,把你写的流程图文本解析成 AST,再交给内部的布局引擎计算节点位置和连线路径,最后生成一段完整的 SVG 字符串。
v10 相比更早版本有个明显变化:顶层 API 被收拢了,mermaid.mermaidAPI那套旧调用方式被逐步移除,统一走mermaid.initialize加mermaid.render或mermaid.run。另外 v10 引入了更严格的安全级别控制,默认不会执行文本里携带的click回调,也不会允许javascript:协议的链接,这些后面会专门展开。
在浏览器里,Mermaid 默认会扫描文档中带有class="mermaid"的代码块,把这些块的内容取出来替换成渲染好的 SVG。但在动态页面里,我更推荐关掉自动扫描,手动控制每张图的渲染时机。原因很简单:单页应用的路由切换、弹窗里的临时图表、后端返回的 Mermaid 文本,这些场景都很容易脱离文档初始扫描的时机,一旦依赖startOnLoad,就会出现图表不显示或者显示错乱的问题。
2.3 render 与 run:两个 API 的分工与选择
mermaid.render和mermaid.run是两个容易混淆的入口,它们的定位完全不同。render(id, text)接收一个字符串形式的图表代码,返回一个 Promise,解析后得到 SVG 字符串,你可以决定怎么把它插到页面上;run({ nodes })则是直接操作 DOM 节点,适合页面上已经存在一批.mermaid块的场景,类似手动触发了一次页面扫描。
我一般会把render当作主入口,因为它对渲染过程的可控性最强。比如后端接口返回了一段 Mermaid 文本,前端拿到后可以先把非法字符过滤一遍,再决定渲染到哪个容器里;如果渲染失败,你还可以捕获异常并展示备用的错误提示,而不是让页面留下一块空白。run更适合管理端这类“页面加载时一次性把整页图都渲染出来”的简单场景,它默认把图表放在节点内部,省去了手动插入的步骤。
两者的输出也有差异:render返回的 SVG 字符串在插入页面时需要用container.innerHTML = svg这种方式;run则直接把 SVG 写进目标节点,适合不想关心内部实现细节的接入方。如果你的业务里既需要动态渲染、又需要静默兜底,就围绕render做一层封装,输出统一的结构,后续做主题切换和事件绑定也方便。
3. 用 v10.6.1.min.js 跑通第一张流程图:最小可复现工程
3.1 最小 HTML:一个文件把文本变成流程图
先不接任何框架,也不上构建工具,一个 HTML 文件把流程跑通。这里的关键是把 Mermaid 文本定义成变量,再用render方法手动渲染。
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8" /> <title>Mermaid v10.6.1 最小渲染示例</title> <script src="./mermaid.min.js"></script> </head> <body> <div id="diagram"></div> <script> // 关闭自动扫描,改用手动渲染 mermaid.initialize({ startOnLoad: false, securityLevel: 'strict', theme: 'base' }); // 这里写的是标准的 Mermaid 流程图语法 const diagramText = ` graph TD A[开始] --> B{是否登录} B -- 是 --> C[进入首页] B -- 否 --> D[跳转登录页] `; async function render() { try { const { svg } = await mermaid.render('diagram-svg', diagramText); document.getElementById('diagram').innerHTML = svg; } catch (e) { console.error('Mermaid 渲染失败:', e); document.getElementById('diagram').textContent = '图表渲染失败,请检查语法'; } } render(); </script> </body> </html>这段代码的逻辑很直白:先初始化 Mermaid,关闭自动加载,再定义一段graph TD语法的流程图文本,最后调用mermaid.render拿到 SVG 并注入到页面上。注意render的第一个参数'diagram-svg'最终会成为 SVG 元素的 id,当你动态渲染多张图时它必须唯一。
几个参数先说清楚。startOnLoad: false是动态渲染的基本前提,否则页面一加载就会自动扫描文档里的.mermaid块,可能和手动渲染互相干扰。securityLevel: 'strict'是默认推荐值,这个级别下外部链接协议和脚本回调都会被限制,之后需要绑定点击事件时再单独改。theme: 'base'表示用基础主题,方便自己覆盖颜色变量。
3.2 从 CDN 到静态资源:v10.6.1.min.js 的两种引入方式
本地示例里我用的是相对路径./mermaid.min.js,实际项目里通常有两种引入方式。一种是直接用 CDN 上的压缩文件,写法是<script src="https://cdn.example.com/mermaid.min.js"></script>。这种方式胜在零构建、上手快,但要注意两点:CDN 不一定每次都能拿到最新文件,代理服务器可能缓存旧版本;内网部署或离线环境无法访问公网,这时就得把文件下载到自己的服务器上。
另一种是 npm 安装后在构建工具里引入。v10 版本的 npm 包同时提供 ESM 和 UMD 产物,在 Vite 或 webpack 工程里可以这样写:
import mermaid from 'mermaid'; mermaid.initialize({ startOnLoad: false, theme: 'neutral' });用 npm 方式的好处是版本能被 package.json 锁定,升级时走正常的依赖管理流程,团队里任何成员拉下代码都能装到一致版本。劣势也很现实:打包时要注意mermaid.min.js本身已经是个压缩文件,不需要再做二次压缩;同时它依赖浏览器 DOM 接口,在服务端渲染或测试环境里直接 import 会报找不到window的错误。
如果只是想在静态页面里快速使用,我的习惯是下载v10.6.1.min.js到项目的vendor目录里。这样既绕开公网依赖,也不用改造构建流程,还能用相对路径在任何环境下稳定加载。放到页面时建议顺便加版本参数,比如/vendor/mermaid.min.js?v=10.6.1,方便线上更新缓存。
3.3 动态渲染:从接口拿文本到 SVG 注入的完整示例
静态页面只能展示固定内容,真实业务里更常见的是后端返回流程描述,前端渲染成图。比如流程编排引擎把你配置的节点和连线拼成 Mermaid 文本,通过接口返回给前端。这时需要注意两个坑:每次渲染的 SVG id 必须不同,否则浏览器 DOM 里会出现重复 id;动态获取的文本不能直接信任,要在渲染前做基础检查。
// 动态渲染工具函数 let renderCount = 0; async function renderMermaid(container, mermaidText) { if (!container || typeof mermaidText !== 'string' || !mermaidText.trim()) { console.warn('渲染条件不满足'); return null; } const id = `flow-${Date.now()}-${renderCount++}`; try { const { svg } = await mermaid.render(id, mermaidText); container.innerHTML = svg; } catch (error) { // 捕获运行时错误,避免阻断业务主流程 container.innerHTML = `<pre style="color:red">Mermaid 语法错误: ${error.message}</pre>`; } }这段封装做了三件事:生成唯一 id,因为 Mermaid 内部会基于这个 id 创建对应的 SVG 根节点;对空文本和非法入参做拦截;捕获渲染异常并回写到容器里,方便用户看到具体的语法问题。实际使用时,从 fetch 拿到接口数据后直接调用renderMermaid(document.getElementById('container'), res.data.diagramText)即可。
要注意mermaid.render是异步操作,连续调用时需要确认上一次渲染已经完成,否则可能出现后来的渲染先返回、先插入 DOM,然后被前一次的结果覆盖。常见的做法是调用时记录一个递增序号,只把最后一次请求的结果放到页面上。这种竞态问题在图表组件里很容易忽视,但一旦出现,表现就是“图表偶尔不正确”。
4. 深度定制:让 Mermaid 流程图可视化更贴近业务
4.1 流程图语法举一反三:方向、节点、连线与子图
Mermaid 流程图语法看着简单,但能表达的结构很丰富。graph TD表示从上到下布局,LR是从左到右,BT从下到上,RL从右到左。节点形状靠括号区分:方括号是矩形、圆括号是圆角矩形、花括号是菱形、双圆括号是圆形,斜杠组合能画平行四边形和六边形。连线方式除了实线箭头-->,还有无箭头---、虚线-.->、粗线==>,以及带文字标注的-- 文案 -->。
下面这段文本基本覆盖了日常常见的表达方式:
const demo = ` graph LR subgraph 用户端 A[浏览器] --> B{登录状态} B -- 未登录 --> C((登录页)) B -- 已登录 --> D[控制台] end subgraph 服务端 E[网关] ==> F[业务服务] F -.-> G[(数据库)] end D --> E `;subgraph的作用是把一组节点包进一个带标题的分区,适合表达系统边界或模块划分,这在画系统拓扑图时几乎是必用语法。ER 图和思维导图也是类似思路,比如 ER 图用erDiagram开头声明实体关系,思维导图用mindmap开头,这些结构在v10.6.1里都原生支持,不需要额外插件。
4.2 主题与样式:用 themeVariables 与主题预设改出统一风格
Mermaid 提供了预设主题,包括default、forest、dark、neutral和base。预设主题开箱即用,但想在图表里融入公司品牌色,就得用themeVariables精确控制颜色。比如让所有节点变成白底黑字、边框颜色统一,可以这样配置:
mermaid.initialize({ startOnLoad: false, theme: 'base', themeVariables: { primaryColor: '#ffffff', // 节点填充色 primaryBorderColor: '#333333', // 节点边框色 primaryTextColor: '#333333', // 节点文字色 lineColor: '#666666', // 连线颜色 fontSize: '16px' // 全局字号 } });如果你觉得节点框整体偏小,Mermaid 并没有直接提供“放大节点”的配置项,但可以用两种方式间接解决。一种是把fontSize调大,文字变大后节点会跟着撑大;另一种是渲染完成后用 CSS 覆盖 SVG 内部样式,比如用#diagram-svg rect { rx: 8px; }调整圆角,或者给.mermaid svg { transform: scale(1.2); }整体放大。要注意transform: scale会改变元素占位尺寸,父容器需要预留足够空间,否则图表会被裁切。
主题配置是全局生效的,如果同一个页面里有多张图需要不同配色,就只能在渲染不同图之前重新调用mermaid.initialize。这种做法会影响所有后续渲染,所以我会把主题切换封装成函数,在切换时重新初始化实例,而不是在全局配置里塞一堆条件分支。
4.3 参与交互:点击节点跳转、绑定回调与 tooltip
流程图画出来不只是给人看的,节点往往对应着具体的业务对象,需要能点击跳转或触发展开面板。Mermaid 在语法层面支持click指令,有两种用法:click A "https://example.com"让节点点击后跳转,或者click A callbackName把点击事件绑定到全局函数。
但这里有个关键限制:默认安全级别下回调不会生效。你需要把securityLevel调整成loose并把回调函数挂在全局作用域,Mermaid 才会把click指令转换成事件监听器。
// 必须把函数暴露成全局属性 window.showOrderDetail = function(nodeId) { console.log('点击了节点: ', nodeId); }; mermaid.initialize({ startOnLoad: false, securityLevel: 'loose', // 允许回调执行 theme: 'base' }); const text = ` graph LR A[订单详情] --> B[用户信息] --> C[支付记录] click A showOrderDetail click B showOrderDetail `;将securityLevel调成loose意味着 Mermaid 不再拦截javascript:协议链接,这在处理不可信文本时有风险。如果你是渲染用户生成的内容,我建议保持严格模式,只用链接跳转,并在渲染前把链接白名单校验一遍;如果确实需要回调,至少保证文本来源可信、回调函数只做前端展示层面的操作,不做敏感数据提交。
5. Mermaid 渲染工具避坑指南:我从 v10.6.1 踩过的血泪经验
5.1 改一行就报错:先看懂这几类高频语法问题
Mermaid 的解析器对语法比较挑剔,少一个空格、多一个引号都会渲染失败,而且报错信息相当不直观。我遇到最多的三类错误是:节点文本里出现了未转义的中文标点或括号,导致解析器把结构打乱;连线两端引用的节点 id 不存在;graph关键字后面的方向写成了小写或者拼写错误。
下面这张表是我排查时的参考顺序:
| 报错特征 | 常见原因 | 处理方式 |
|---|---|---|
Parse error on line 1或类似解析错 | graph TD后面漏了换行,或语法关键字拼错 | 先单独打开官方在线编辑器验证段语法 |
| 提示某个 node id 找不到 | 连线左右引用了未定义的节点 | 检查每个节点是否先声明后连线 |
运行时Cannot read properties of undefined | 文本为空、变量未传、容器不存在 | 确认传入的是字符串且有实际内容 |
| 中文文字显示成乱码或方块 | 页面编码不是 UTF-8,或字体库缺失中文 | <meta charset="UTF-8">,并给 SVG 指定中文字体 |
遇到语法问题我的处理顺序是:先在页面上把 Mermaid 文本原样打出来,用官方 Mermaid Live Editor 复现一遍。如果官方编辑器能渲染而项目里不能,那问题基本出在引入方式或初始化配置上,而不是语法本身。
5.2 同一页面多张图互相干扰:id 重复与实例冲突
现象是页面上有多张图,渲染第二张时第一张图消失,或者控制台报 “ID already exists”。原因很直接:mermaid.render生成的 SVG 带有以传入 id 为前缀的节点,比如你传flow-1,内部会创建flow-1-svg。如果第二次还传flow-1,浏览器里会出现两个相同 id 的元素,后续的样式和事件绑定就会错乱。
解决方案是每次渲染都用递增计数器生成全局唯一 id,比如flow-0、flow-1、flow-2。如果做了封装,还要考虑组件卸载后旧 SVG 没有被清理,重新进入页面时 DOM 里的残留节点会被浏览器保留。React、Vue 这类框架控制容器内容时通常不会遇到这个问题,因为虚拟 DOM 会对比差异并替换内容,但原生innerHTML注入时就要自己在渲染前清空容器。
5.3 CDN 缓存引发的版本错乱:本地正常,线上却是旧功能
这是一个非常容易翻车的地方:本地调试时用最新语法写得顺风顺水,部署到线上后一段新语法渲染不出来,打开控制台才发现加载的还是几个星期前的旧版本文件。原因多半是 CDN 节点缓存了旧资源,或者浏览器本地缓存未失效。
我一般会在所有静态资源引用上使用精确版本号,比如/vendor/mermaid.min.js?v=10.6.1,同时在响应头里确认Cache-Control的配置。更稳妥的做法是把mermaid.min.js下载到自己的静态资源目录里,部署由发布流程控制,不会出现“代码上线了但第三方 CDN 还没同步”这类问题。如果必须用公网 CDN,就把版本号写死在 URL 里,禁止用latest这类不带版本的方式。
5.4 Markdown 预览与 Mermaid 的集成坑:Typora、VS Code、Obsidian
很多人遇到的问题是:在 Typora 里明明能正常预览的 Mermaid 图,换到 VS Code 的 Markdown 预览插件里就空白,或者反过来。这是因为这些编辑器和插件的 Mermaid 支持分两种实现方式:一种是内置了某个版本的 Mermaid 运行时,另一种是动态引入外部的mermaid.min.js。不同的预览实现版本不同,对语法特性的支持自然有差异。
在 Typora 里要更新 Mermaid,需要关注 Typora 版本的更新日志,它不会单独提供一个“升级 Mermaid”的按钮;VS Code 安装 Mermaid 插件时,注意插件是否支持自定义<script>引入,如果有配置项,可以指定到你信任的版本路径;Obsidian 的 Mermaid 预览依赖应用内置版本,如果遇到新版语法不支持,只能等应用升级或绕开这个图表。这个问题的本质是:文档工具内置的 Mermaid 版本往往落后于官方最新版。
我给你的建议是:不要在 Markdown 里写过于新潮的语法。如果你的文档主要给别人用,状态机、饼图这类相对小众的图尽量不做主依赖,否则对方换个预览插件就看不了。业务系统里的动态渲染走自己的工程文件,不受编辑器的版本限制。
5.5 securityLevel 与外部链接限制:为什么点击事件不触发
表现是代码里写了click A "https://xxx",节点也确实渲染出来了,但点击没有任何反应;或者在控制台看到SecurityError。原因是 Mermaid 的securityLevel改变了链接协议的白名单范围。strict模式下,javascript:协议完全被禁用,外部链接也只能是http、https、mailto等白名单协议;default模式相对宽松一点,但仍不建议在渲染不可信内容时放开。
想恢复点击跳转,可以把securityLevel调整为loose,但你要清楚这意味着什么:任何javascript:协议都会被执行,恶意文本拿到页面权限后可以读取当前页面数据。我的原则是:只有渲染完全受控的文本时才用loose,并配合做协议白名单检查;如果文本来自接口且可能被用户编辑,保持strict,宁可牺牲交互,不赌安全性。
6. 进阶技巧:版本锁定、懒渲染与自动重绘
在真实项目里,光会调用render还不够,我建议你再往前走三步。第一步是版本锁定和完整性校验。下载mermaid.min.js之后,用openssl dgst -sha384 -binary mermaid.min.js | base64生成 SRI 哈希,挂到<script>标签上,这样即使被劫持,浏览器也能识别文件被篡改。第二步是懒渲染。如果页面里图表很多,初始渲染全部图会拖慢首屏,用 IntersectionObserver 让图表进入视口时才调用renderMermaid,视线往下拨时按需出图。
// 懒渲染示例:监听容器进入视口 const observer = new IntersectionObserver((entries) => { entries.forEach(async (entry) => { if (!entry.isIntersecting) return; const el = entry.target; observer.unobserve(el); await renderMermaid(el, el.dataset.diagram); }); }); document.querySelectorAll('[data-diagram]').forEach(el => observer.observe(el));第三步是处理动态内容。SPA 里路由切换后新插入的图表容器不会自动渲染,我习惯在切换完成后手动触发一次渲染;如果图表是异步加载的,就在数据返回后再调用。不要依赖全局startOnLoad自动扫描,它会把你拖进“偶尔不显示、刷新又好了”的玄学里。以前我也图省事用自动扫描,后来被线上几次“图表消失事件”折腾过,现在所有渲染都收口在一个工具函数里,统一管理 id、错误提示和主题配置。这个习惯让我少踩了很多坑,希望帮到你。
本文还有配套的精品资源,点击获取