
简介面向 Drawio 桌面用户的 Mermaid美人鱼图表集成插件基于 JavaScript 开发能够将饼状图、顺序图、甘特图、状态图、流程图、类图等常见图表的绘制过程简化成一行行简洁的标记语言脚本双击形状编辑后保存即可自动重绘解决了手动绘图耗时长、协作修改困难的问题。压缩包共 45 个文件大小仅 1.98MB内部包含 js 插件源码、mmd 脚本样例、png 与 gif 效果预览、drawio 工程文件、json 配置以及 md 说明文档等不同类型文件各自承担代码、演示、配置和文档职责便于按需查阅和二次开发。当前已有 1292 人学习下载。借助包内完整示例图、在线演示与测试工程读者可以快速掌握双击形状编辑美人鱼脚本、离开编辑器后自动重绘的操作流程并利用所有配置选项映射为 draw.io 形状属性的特性自由定制图表样式与交互行为。无论是日常绘图还是深度集成到 Drawio 桌面的二次开发场景都能获得直接可用的参考素材与可运行代码。 上周一个同事把一段Mermaid代码甩给我说帮我把这张流程图画出来。我打开drawio鼠标悬在半空心里清楚得很手动拖矩形、连线、调布局一套下来半小时就没了而且改个文字还要来回点选。Mermaid的优势恰恰在这——几行代码就能把节点和关系说清楚但drawio又不认这个格式。于是就有了这个念头给drawio桌面版做一个能识别Mermaid代码的插件把代码粘贴进去自动生成可编辑的图形。Mermaid中文圈子里经常喊它美人鱼其实和鱼没什么关系就是用文本描述图表的一种语法。这篇文章就把我的实现思路、踩坑记录和完整流程都捋一遍适合正在用drawio画各种架构图、流程图又不想浪费太多时间在纯手动拖拽上的朋友。1. 先弄清痛点Drawio和Mermaid到底差在哪1.1 Drawio图形化拖拽的效率瓶颈drawio桌面版几乎是程序员和非技术同事之间沟通图形的一种通用语言。它支持各种图形模板导出PNG、SVG、PDF都方便还自带XML格式也能直连各种网盘。但它的交互模式仍然是鼠标拖拽为主画一张稍微像样的图要么靠对齐线小心翼翼地调整要么靠选中多个节点然后挨个改坐标。我见过不少同事画一张二十个节点的架构图光调格式就花了四十分钟。它更别扭的一点是图一旦复杂起来改版成本很高。比如流程图里要加一条线、改一个判断框牵一发动全身手动调整的边际成本真的不低。久而久之大家就会想找个更高效的方式来定义图形。1.2 Mermaid的代码优势与生态局限Mermaid是另一种画图路子用文本定义图表。节点、连线、子图全部写在纯文本里。它的语法学起来很快核心概念不超过十个。在线编辑器mermaid.live或者VS Code的插件都能边写边预览配合git做版本管理改动一目了然。但Mermaid的局限性也很明显生成出来的图大多是一次性的。想继续在已有基础上手动微调不行你得回到代码里改然后重新渲染。想和drawio里已有的其他元素混排、连接也不行。它的生态相对孤立大多停留在Markdown文档和各类在线预览器里。这就导致很多团队里出现一种尴尬局面文档里用Mermaid画图很快但需要交付一张能二次编辑的正式图时又只能回到drawio里手动重画。1.3 插件要解决的三个核心问题这个插件的目标很明确让drawio能读Mermaid代码并把代码转成真正可编辑的drawio图形。我给自己列了三个必须解决的问题输入方式要足够简单插件菜单里点一下粘贴代码就跑。生成的图形必须是原生可编辑的不是贴一张图片进来而是每个节点、每条连接线都能在drawio里继续改。布局不能太难看至少不能所有节点堆叠在左上角得用drawio的自动布局算法做一个初步整理。这三个需求看起来不复杂但真正实现起来还是会踩不少坑下面从头拆解。2. 插件原理Mermaid代码如何变成Drawio图形2.1 Drawio桌面版的插件加载机制drawio桌面版本身基于Electron架构支持通过JavaScript插件扩展功能。插件本质上是一个JS文件通过配置或启动参数让应用在启动时加载它加载后会执行Draw.loadPlugin回调并拿到一个ui对象。这个对象就是drawio主界面的核心句柄通过它可以拿到当前的graph实例、注册菜单、添加按钮、弹窗等。实际开发中我是先把插件文件放到一个固定目录再通过配置让应用启动时加载。不同版本的入口略有差别我以Windows下当前稳定版为例打开drawio后在顶部菜单里找到 Extras - Configuration会打开一个JSON配置编辑器在里面加上plugins数组指向本地插件文件路径。如果没有这个入口也可以用命令行参数--plugins/path/to/plugin.js来加载。桌面版另外有一个好处——插件加载成功后会有明确的UI反馈所以调试起来比纯命令行工具直观得多。2.2 三条技术路线本地渲染桥接、完整库内嵌、轻量解析写插件的第一步是定技术路线。我搜了一圈市面上常见的方案其实有三种我最终权衡下来选了第三种。方案A是调用Mermaid官方CLI先装好Node.js和mermaid-cli用命令行把Mermaid代码渲染成SVG文件再让drawio导入SVG。好处是语法支持完整坏处是drawio插件本身跑在Electron渲染进程里直接调系统命令很别扭还得依赖外部环境用户换台电脑就废了。方案B是内嵌完整Mermaid解析器把Mermaid的JS库整个打包进插件在drawio里实时解析渲染。语法支持最完整但Mermaid库体积不小而且和drawio的插件API整合起来非常费力调试成本高只是为了导入一张图就把整个渲染引擎搬进来有点杀鸡用牛刀。方案C是轻量解析器只解析流程图flowchart中常用的节点和连线语法转换成drawio原生图形。功能上肯定没有完整Mermaid广但胜在实现简单、稳定、不依赖外部环境。对大部分日常画图场景架构图、流程图、时序简图完全够用。我最终选了它。三条路线的对比大概是这样技术路线语法支持外部依赖实现难度我的推荐度方案A调用mermaid-cli完整Node.js CLI较多中低方案B内嵌完整Mermaid库完整需打包大体积JS库高低方案C轻量解析器drawio原生图形常用流程图语法无低高2.3 轻量方案的三个核心模块整个插件拆成三块语法解析模块接收Mermaid文本输出节点列表和边列表。这里不需要完整的AST语法树正则做词法拆解就够关键是能识别常见的图形语法。图形生成模块把节点列表映射成drawio的cell边映射成connection并设置对应的形状样式。布局计算模块插入完所有节点和边之后调用drawio内置的mxHierarchicalLayout做一次分层布局让图能看得过去。这样模块化设计的好处是以后想扩展支持子图、支持时序图只需要在语法解析模块里加规则其他模块基本不用动。3. 实操手写一个Mermaid导入插件3.1 环境准备与确认当前版本我这边实操用的环境是Windows 10加drawio桌面版最新稳定版。如果你用的是旧版本可能配置入口和部分API有差异但整体思路通用。装好drawio之后先建一个专门放插件的目录比如D:\drawio_plugins。然后在配置里加一个插件路径。如果配置编辑器里没有内容可以粘贴下面这个最小JSON{ plugins: [ { url: file:///D:/drawio_plugins/mermaid_plugin.js } ] }注意路径要写绝对路径Windows上路径分隔符和斜杠的转义容易踩坑建议直接把JSON粘贴进配置编辑窗口保存drawio会自动校验格式。配置完成后重启drawio看到顶部菜单栏多出Mermaid说明插件加载成功。如果没多出来按文末第4章的排查步骤走。3.2 插件骨架注册菜单与弹窗交互插件的基本结构如下Draw.loadPlugin(function(ui) { var graph ui.editor.graph; ui.menubar.addMenu(Mermaid, function(menu) { menu.addItem(从Mermaid代码导入, null, function() { openImportDialog(); }); }); function openImportDialog() { var dlg new mxWindow(从Mermaid导入, null, 300, 200, 600, 400); var textarea document.createElement(textarea); textarea.style.width 560px; textarea.style.height 340px; textarea.placeholder 粘贴 Mermaid flowchart 代码例如\ngraph TD\n A[开始] -- B{判断}; dlg.content.appendChild(textarea); dlg.setClosable(true); var btn document.createElement(button); btn.textContent 生成; btn.onclick function() { convertToGraph(textarea.value); dlg.close(); }; dlg.content.appendChild(btn); dlg.setVisible(true); } });这里用到了mxWindow做弹窗简化起见我用一个textarea让用户粘贴代码点击按钮触发转换。实际项目里如果你想让体验更好也可以改成支持选择 .mmd 文件读取但在插件里访问本地文件系统需要额外的桥接处理粘贴文本是最稳的MVP方案。mxWindow的构造参数在不同版本里略有差异如果发现窗口没显示优先检查drawio的调试控制台报错。3.3 解析Mermaid语法正则解析节点和关系这个功能一个标准Mermaid flowchart代码大概长这样graph TD A[开始] -- B{是否合法} B --|是| C[通过] B --|否| A这是整个插件里最容易翻车的地方。Mermaid的语法看着简单但实际写出来风格很自由有人喜欢一行一个节点定义有人喜欢把边写在节点定义后面还有人喜欢用中文标签不加引号。所以解析器不能写得太死。我采用了两步走方案先扫出所有节点定义再扫出所有边定义。节点定义的常见形态包括A[开始]矩形A(开始)圆角矩形A{判断}菱形A[[子程序]]双线矩形对应正则大致长这样var nodePatterns [ { regex: /([A-Za-z0-9_\u4e00-\u9fa5])\s*\[\[(.?)\]\]/g, shape: rectangle;strokeWidth2; }, { regex: /([A-Za-z0-9_\u4e00-\u9fa5])\s*\{([^}])\}/g, shape: rhombus;perimeterrhombusPerimeter; }, { regex: /([A-Za-z0-9_\u4e00-\u9fa5])\s*\(([^()])\)/g, shape: rounded1; }, { regex: /([A-Za-z0-9_\u4e00-\u9fa5])\s*\[([^\[\]])\]/g, shape: rectangle; } ];这里允许中文标签所以ID的正则里加上了\u4e00-\u9fa5。但有个坑A(开始)这种圆角矩形和箭头混在一起时正则容易误匹配。所以我会先去除每一行里箭头符号后面的部分再做节点扫描。边可以用另一组正则来收集var edgePattern /([A-Za-z0-9_\u4e00-\u9fa5])\s*(?:--|---|-.-||--\s*[^]?--)\s*(?:|\|([^|])\|)\s*([A-Za-z0-9_\u4e00-\u9fa5])/g;带标签的边比如A --|是| B还需要把标签文本提取出来后面作为边上显示的label。解析完之后整理成两个数组返回给图形生成模块。3.4 生成图形插入节点、连线和自动布局拿到节点和边之后接下来就是往drawio图形模型里塞东西。所有插入操作必须放在beginUpdate和endUpdate之间否则undo栈会乱掉。我先按临时坐标插入所有节点再插入边最后执行一次分层布局function convertToGraph(code) { var parsed parseMermaid(code); if (!parsed || parsed.nodes.length 0) { alert(没有解析到有效节点请检查Mermaid语法); return; } var parent graph.getDefaultParent(); graph.getModel().beginUpdate(); try { var cellMap {}; var i 0; parsed.nodes.forEach(function(node) { var x 40 (i % 5) * 180; var y 40 Math.floor(i / 5) * 100; var style whiteSpacewrap;html1; node.shape; var cell graph.insertVertex(parent, null, node.label, x, y, 120, 60, style); cellMap[node.id] cell; i; }); parsed.edges.forEach(function(edge) { var source cellMap[edge.from]; var target cellMap[edge.to]; if (source target) { graph.insertEdge(parent, null, edge.label || , source, target, edgeStyleorthogonalEdgeStyle;); } }); var layout new mxHierarchicalLayout(graph); layout.execute(parent); } finally { graph.getModel().endUpdate(); } }这里有几个细节值得说清楚。第一mxHierarchicalLayout执行时要求所有节点和边都已经插入模型所以顺序上必须是先节点、再边、最后布局。第二临时坐标我用了简单的5列网格排布布局算法执行后会完全重排坐标这样用户看到的第一眼就是整理好的图而不是一堆叠在一起需要手动拖开的乱麻。第三边默认用正交拐角样式和drawio里常用的流程图风格一致视觉上更舒服。3.5 完整代码结构与调用流程整理一下完整的调用流程启动时加载插件注册Mermaid菜单用户点击菜单项弹窗收集Mermaid代码转换函数里先解析语法再插入图形数据最后自动布局。如果你还想加一点实用性可以在解析失败时输出具体的行列提示帮用户定位Mermaid语法错误。我建议把解析模块单独拆成一个文件因为在写的过程中你会发现需要不断补充语法规则。比如后续想支持subgraph子图只需要在解析器里识别到subgraph块后把它视为一个分组容器节点再在该容器内插入子节点即可。这样主入口代码一直保持简洁扩展点都集中在解析器里。4. 常见问题与排查实录4.1 插件菜单不显示怎么办这是最常遇到的问题。先分清是配置没生效还是插件文件报错了。配置没生效的话重启前先打开配置编辑器确认JSON能被drawio正确保存如果保存后重启还是没有Mermaid菜单检查插件文件路径是否包含中文字符或空格路径里的特殊字符很容易导致加载失败我建议插件目录和文件名全部用英文。如果配置看起来没问题就按CtrlShiftI打开开发者工具切到Console标签页看有没有红色报错。常见问题是插件文件本身语法错误或者插件代码里使用了某些drawio对象但调用时机不对。把Draw.loadPlugin回调外的全局代码都收进回调函数里能避免不少奇怪的初始化问题。注意drawio的插件是启动时一次性加载的修改插件文件后必须完全退出再重启不是简单刷新页面就能生效。4.2 Mermaid语法解析失败的场景我刚开始写解析器的时候老老实实按Mermaid官方的BNF语法来结果发现用户写的代码千奇百怪标签里有中文括号、有换行、有特殊字符还有人写A--B和A -- B混着来。后来我把解析策略放宽松了——先按行处理行内先去掉注释%%开头的内容再做正则匹配对无法解析的行直接跳过只在最后统计里提示有x行未识别。这样容错很高代价是有时候误解析会悄悄吞掉你不想吞的节点。实际上可以通过给用户返回一个解析报告来解决但MVP阶段我选择尽量不打断创建流程。如果你需要解析更复杂的语法比如加入了子图或不同箭头样式建议引入一个成熟的Mermaid解析库做词法分析而不是持久战式地堆正则。正则适合快速验证撑到大上百行代码时维护成本会明显上升。4.3 布局混乱和性能卡顿的解决方案布局混乱的根源大多是插入顺序和布局算法不匹配。比如有些节点还没插入完就执行了布局或者边引用了不存在的节点ID。我在代码里用cellMap保存所有节点引用布局前先做一轮存在性校验从源头避免了幽灵边。性能问题主要体现在大图上一次性解析超过200个节点时beginUpdate/endUpdate之间的批量插入完全没有问题但mxHierarchicalLayout的计算量会明显增加。我的处理办法是给布局算法加一个节点数阈值超过150个节点就跳过自动布局改为按解析顺序生成网格坐标让用户自己用drawio自带的布局按钮二次整理。这样在超大图场景下也不会卡死应用。4.4 生成文件异常报错时先别慌用插件生成图形的时候偶尔会把drawio搞到报非绘图文件错误。这类报错多半是某个节点或边的XML属性不合法比如style里混入了不可见的控制字符。排查时最有效的办法是把画布不断缩小看是哪个区域图形异常或者打开文件的XML源码直接搜索异常片段。插件生成的图用到的大部分属性都是drawio原生支持的只要不手动往style里塞奇奇怪怪的键值基本不会触发这个问题。另外提醒一句drawio文件正常双击就能打开如果遇到打不开的情况优先备份原文件再操作。这个插件折腾下来我最大的体会是工具链的打通不一定要靠完整实现去解决问题能用最小代价解决80%的日常痛点就值得先落地。Mermaid和drawio的定位本来就不完全一样硬要做成完整替代Mermaid渲染器的插件投入产出比反而不高。但如果你只是想让这两者之间的连接不再靠手动这个轻量方案已经很能打了。最后分享一个小技巧我平时写Mermaid代码会用在线预览器或VS Code插件先把图画出来确认无误后再粘贴进drawio生成最终的可编辑版本。这样流程兼顾了Mermaid的快和drawio的可精细调整是现在的日常画图习惯。未来如果还有精力我会考虑给插件加上子图支持和导出Mermaid的逆向转换那样整个工作流就彻底闭环了。本文还有配套的精品资源点击获取