Line9 是一个自带布局能力的 Mermaid 渲染引擎,核心变化在于它不再依赖 Mermaid 默认的 dagre 布局方案,而是用自己的布局算法去完成节点排列、边路由和子图组织。对经常写 Mermaid 代码、被节点重叠和边交叉折腾过的人来说,“渲染引擎”四个字里真正值钱的,其实是后面那半句“自带布局”。
这类项目最值得先看的不是功能列表,而是能不能在普通环境里稳定跑起来、输出是否真的比默认方案清楚。下面我按自己验证一个渲染引擎的顺序来拆:先理解渲染链路,再对比差异,然后确认环境、逐步落地、验证布局质量,最后说排查和建议。如果你正在做在线文档、流程图编辑器,或者正在纠结要不要把 Mermaid 默认渲染换掉,这篇应该能帮上忙。
1. 先搞清楚 Mermaid 的渲染链路:为什么 layout 是核心
1.1 从一段 mermaid 代码到一张图,中间发生了什么
Mermaid 的渲染链路可以简单分成四层。第一层是语法解析,把graph TD; A-->B这样的文本解析成结构化的中间表示;第二层是图模型,把节点、边、子图、样式信息整理成布局引擎能读的数据;第三层是布局计算,决定每个节点放在哪个坐标、每条边怎么走;第四层才是把坐标数据画成 SVG 或 Canvas。
很多人以为 Mermaid 只是把字符串转成 SVG,所以当渲染结果不好看时,第一反应是“换个主题”或“调一下方向”。实际上,图长什么样,百分之八十在布局层就定了。解析只负责“有没有语法错误”,布局负责“好不好看”。这也是为什么一个自称 rendering engine with its own layout 的项目,核心卖点一定在 layout,而不是 parsing。
不同图型的布局逻辑差别很大。flowchart 走的是有向图布局;class 图、state 图各有规则;sequence 图基本靠参与者和消息顺序排时间轴,不需要复杂坐标计算。所以自研布局引擎通常优先覆盖 flowchart,这也是最常被拿来做对比的场景。如果你平时主要在写流程图,这个方向和你关系最大。
1.2 layout 难在哪:为什么不是“把节点放上去”这么简单
布局难在几个很具体的点上。第一,节点有尺寸。文本会换行,字体宽度在不同平台不一样,节点本身可能是圆角矩形、菱形、六边形,尺寸变化会直接影响边从哪里穿出来。第二,边不能乱穿节点。理论上一条边可以走很多路径,但视觉上不能跨过无关节点。第三,减少边交叉是个计算上很难的问题,工程实现基本靠启发式算法,没有绝对最优解。
还有子图。Mermaid 里可以用subgraph把节点聚类。布局引擎要把子图当成一个有边界的容器,同时保证子图内部紧凑、外部不与其他节点重叠。这个需求在 dagre 里经常表现一般,很多自研引擎想解决的就是这个点。
最后是确定性。同一个图,用户希望每次渲染出来的坐标完全一致。如果布局算法里引入了随机初始化,或者依赖外部库的版本行为,截图对比、快照测试都没法做。所以好的布局引擎,默认输出必须稳定可复现。
2. 自研布局引擎相比默认方案,差异到底在哪
2.1 默认渲染的常见痛点
默认 Mermaid flowchart 的布局依赖 dagre。dagre 是经典的有向图布局库,处理中小型图问题不大,但维护节奏比较慢,很多社区反馈的边交叉、长边绕路、子图布局问题长期存在。在 mermaid live editor 里画一个二十个节点的图,很容易看到边从上到下绕一大圈再去目标节点的情况。
节点多以后,dagre 还会出现两个问题:一是布局时间明显上升,二是结果有时会突然变化。同一段代码,在 VSCode 的 mermaid preview 插件里看是一种效果,在 live editor 里看是另一种效果,很多人误以为是插件问题,实际上很多时候是布局引擎版本差异或者渲染环境差异。
这并不意味着默认方案不能用。中小型图、演示文档、接口文档里的简单流程图,默认渲染完全够用。但如果你做的是面向用户的流程图编辑器,或者要对大量文档做截图回归,这些痛点就会变成实际成本。
2.2 自研布局带来的实际价值
自带布局最大的价值是确定性。同一段 mermaid 代码,输入相同,输出坐标必须相同。这一点对自动化测试特别重要。用快照测试时,只要布局有随机性,diff 就会莫名其妙地失败,最后你只能把整个截图测试删掉。
第二是可控性。默认布局给用户的参数很少,基本就是方向、主题、间距。自研引擎可以针对业务场景定义自己的布局规则。比如审批流程图,你可以要求所有节点必须按层级严格对齐,边尽量走正交线。通用布局库很难满足这种细粒度要求。
第三是性能与体积。如果自研引擎做了轻量化设计,可以去掉 dagre、d3 这类较重依赖,对浏览器端打包体积、加载时间都有帮助。大图场景下,也可以针对特定图型做剪枝或分块计算,性能上比通用算法更有发挥空间。
2.3 也要看到代价
自研布局不是没有成本。第一,Mermaid 语法还在演进,自研解析器很难做到百分百兼容。第二,Mermaid 生态里的主题系统、自定义样式、点击事件、tooltip、导出功能,自研引擎不一定都实现了。第三,一个刚发布、还需要大量真实场景验证的引擎,和经过多年生产验证的默认渲染器相比,边界情况、浏览器兼容、社区资料都有差距。
所以对这个项目,正确的态度是“先验证,再替换”,而不是看到就想全量迁移。功能列表再漂亮,也要落到你自己的输入样本和运行环境里才算数。
3. 尝试 Line9 之前,先把环境和接入方式确认清楚
3.1 运行环境与模块形态
这类引擎通常有两种使用方式:浏览器端渲染和 Node 端服务端渲染。先确认它支持哪种。如果是 npm 包,要看它发布的是 ESM 还是 CommonJS,因为不同打包工具处理方式不一样。如果你的项目是 Vite、Webpack、Next.js 或 Nuxt,模块格式会直接影响能不能直接 import。
还要注意浏览器环境里的字体测量问题。布局引擎要计算文本宽度,通常会用 DOM 或 canvas 的 measureText。在 Node 里没有 DOM,就需要依赖某种字体测量方案,如果字体和浏览器不一致,布局结果也会有偏差。所以如果你打算在服务端渲染 SVG 再吐到前端,务必对比同一段代码在两种环境下的输出。
如果项目说明里没有给出明确的版本兼容表,建议落地时先跑一个最小示例,确认依赖版本和你当前的 Mermaid 版本是否匹配,不要直接拿生产项目的 mermaid 代码一把梭。这个步骤花不了十分钟,但能避开后面大部分集成问题。
3.2 Mermaid 语法兼容范围
Mermaid 语法本身经历过多次变化,不同版本对某些写法的支持不一样。自研引擎解析 Mermaid 语法时,很可能只覆盖了核心语法子集。需要重点确认的语法点包括:graph TD/LR方向声明、节点形状、subgraph子图、classDef样式定义、linkStyle连线样式、注释写法,以及A-->|label|B这种边上带标签的写法。
与其去翻官方完整语法手册,不如把高频语法点做成一个样例矩阵逐个过一遍。比如从 drawio 转出来的 mermaid 代码,可能带有一些平台特有写法,转换后也要重新确认。最笨但最有效的办法,是准备一批测试用例,把每个语法点单独