Mermaid ELK 布局引擎深度解析:@mermaid-js/layout-elk 的版本演进、配置参数与渲染管线
2026/9/7 7:07:04 网站建设 项目流程

Mermaid ELK 布局引擎深度解析:@mermaid-js/layout-elk 的版本演进、配置参数与渲染管线

【免费下载链接】mermaidGeneration of diagrams like flowcharts or sequence diagrams from text in a similar manner as markdown项目地址: https://gitcode.com/GitHub_Trending/me/mermaid

本文基于仓库中 packages/mermaid-layout-elk/CHANGELOG.md 的完整版本记录,系统梳理@mermaid-js/layout-elk包的演进脉络(0.1.1 → 0.2.3),逐一拆解各版本引入的elk.*配置项(mergeEdgeskeepEntryNodeOnTopnodePlacementAlignment等)在源码中的真实接线方式,并深入渲染管线(ELK 图构建、跨子图边处理、边裁剪与圆角折线生成),帮助你在项目或站点中正确接入并使用 Mermaid 的 ELK 布局引擎。

一、包的定位与包元信息

@mermaid-js/layout-elk是为 Mermaid 提供的基于 Eclipse ELK(通过elkjs)的布局引擎插件包。它不是独立的图表库,而是一个布局加载器(Layout Loader):主包mermaid保留原有的 Dagre 布局,用户按需注册 ELK 加载器后即可用layout: elk配置或flowchart-elk语法切换布局。

packages/mermaid-layout-elk/package.json 中的关键元信息:

字段说明
name@mermaid-js/layout-elknpm 包名
version0.2.3与 CHANGELOG 中最新版本一致
moduledist/mermaid-layout-elk.core.mjsES Module 入口
typesdist/layouts.d.ts类型声明入口
dependenciesd3 ^7.9.0elkjs ^0.9.3依赖 d3 曲线工具与 ELK 打包版内核
peerDependenciesmermaid ^11.0.2要求主包 11.x(对应 0.1.1 起)

包 README(packages/mermaid-layout-elk/README.md)特别提示:支持 mermaid 的宿主站点默认不会提供 ELK 布局,需要自行安装该包才能启用layout: elk

支持的五种布局算法

从 layouts.ts 的源码可直接确认注册表:

const algos = ['elk.stress', 'elk.force', 'elk.mrtree', 'elk.sporeOverlap']; const layouts: LayoutLoaderDefinition[] = [ { name: 'elk', loader, algorithm: 'elk.layered', // `elk` 别名实际跑的是 layered 分层布局 }, ...algos.map((algo) => ({ name: algo, loader, algorithm: algo })), ];

即五个可选值:elk(默认,等价elk.layered分层布局)、elk.stress(力导向/应力布局)、elk.forceelk.mrtree(多根树布局)、elk.sporeOverlap(Spore 重叠布局)。注意elk.layered本身也作为算法名传递给 ELK,别名与算法名在 render.ts 中通过preparedLayout.algorithm解析(见下文管线部分)。

二、接入方式与三种启用写法

2.1 启用 ELK 布局的三种写法

按 README 记载,任选其一即可让普通flowchart走 ELK:

写法 1:切换图表类型前缀

flowchart-elk TD A --> B A --> C

写法 2:frontmatter 配置layout: elk

--- config: layout: elk --- flowchart TD A --> B A --> C

写法 3:frontmatter 指定具体算法(如 stress)

--- config: layout: elk.stress --- flowchart TD A --> B A --> C

2.2 与打包器集成

npm install @mermaid-js/layout-elk
import mermaid from 'mermaid'; import elkLayouts from '@mermaid-js/layout-elk'; mermaid.registerLayoutLoaders(elkLayouts);

核心 API 是mermaid.registerLayoutLoaders(...):把上面layouts.ts导出的LayoutLoaderDefinition[]注册进主包,之后解析器遇到layout: elk*配置或xxx-elk图表类型时,才会按需(loader函数动态import('./render.js'))加载 ELK 渲染器。

三、版本演进全记录(CHANGELOG 逐版本解读)

CHANGELOG 采用 changesets 生成的 Patch/Minor 分组格式。下面按时间倒序完整继承每一条记录,并结合源码说明其实际落点。

3.1 0.2.3(对应 mermaid 11.17.0)——两项新增配置

  1. elk.keepEntryNodeOnTop(feat):让递归流(自环回流图)的入口节点固定在顶层。
  2. elk.nodePlacementAlignment(feat):暴露 BK 分层对齐策略配置。

这两项在 defaultConfig.ts 中都有默认值:

elk: { mergeEdges: false, // 见 3.2 nodePlacementStrategy: 'BRANDES_KOEPF', nodePlacementAlignment: 'NONE', // 0.2.3 新增项,默认 NONE forceNodeModelOrder: false, considerModelOrder: 'NODES_AND_EDGES', keepEntryNodeOnTop: false, // 0.2.3 新增项,默认关闭 },

3.2 0.2.2(对应 mermaid 11.16.0)——mergeEdges 子图传播修复

fix(elk): propagateelk.mergeEdgesconfig to subgraphs in ELK layout — previously edges defined inside a subgraph were not merged even whenelk.mergeEdges: truewas set.

修复前:即使全局设置elk.mergeEdges: true,定义在子图内部的边也不会被合并(多条同起同止的边会各自画线,视觉冗余)。修复后该配置正确下传。源码印证在 render.ts 的buildSubgraphLayoutOptions中——子图的 layoutOptions 显式读取了同一份elkConfig

const layoutOptions: Record<string, unknown> = { 'spacing.baseValue': 30, 'nodeLabels.placement': '[H_CENTER V_TOP, INSIDE]', 'elk.layered.mergeEdges': elkConfig?.mergeEdges, // ← 传播到子图 'nodePlacement.strategy': elkConfig?.nodePlacementStrategy, 'elk.layered.nodePlacement.bk.fixedAlignment': elkConfig?.nodePlacementAlignment ?? DEFAULT_NODE_PLACEMENT_ALIGNMENT, // ← 0.2.3 新增 };

而根图在createRootElkGraph(render.ts)中同样读取:

'elk.layered.mergeEdges': data4Layout.config.elk?.mergeEdges,

3.3 0.2.1(对应 mermaid 11.13.0)——ELK 边默认改为圆角折线

fix: use rounded right-angle edges for ELK layout —— ELK 布局的边从继承全局basis默认曲线,改为默认rounded(直角段 + 圆角转折),修复了 ELK 边"弯曲而非直角路由"的问题(issue #7213)。非 ELK 布局不受影响,仍保留原basis默认。

源码印证:render.ts 在把 ELK 返回的边段写回LayoutData时硬编码:

layoutEdge.curve = 'rounded';

3.4 0.2.0(对应 mermaid 11.11.0,Minor)

feat: Update mindmap rendering to support multiple layouts, improved edge intersections, and new shapes

思维导图渲染升级为支持多布局(ELK 是其中之一)、改进边交叉、新增形状。此版本把layout-elk从 patch 序列提升到 minor,因为它开始承载 mindmap 的新布局能力。

3.5 0.1.9(对应 mermaid 11.10.0)——模型顺序控制配置暴露

两条记录共同构成一组能力:

  • elk.forceNodeModelOrder/elk.considerModelOrder暴露到 mermaid 配置(feat);
  • 默认行为改变:ELK 不再"强制"按代码书写顺序排节点,而是"强烈参考"该顺序(make elk not force node model order, but strongly consider it instead)。

对应默认值forceNodeModelOrder: false+considerModelOrder: 'NODES_AND_EDGES'(defaultConfig.ts)。接线位置在根图选项(render.ts):

'elk.layered.crossingMinimization.forceNodeModelOrder': data4Layout.config.elk?.forceNodeModelOrder, 'elk.layered.considerModelOrder.strategy': data4Layout.config.elk?.considerModelOrder,

3.6 0.1.8(对应 mermaid 11.7.0)

Make elk respect the order of nodes based from the code

开始让 ELK 尊重源码中节点的声明顺序——这是 0.1.9 中"模型顺序"系列配置的前身:先有硬尊重,随后演进为可配置的 force/consider 两级策略。

3.7 0.1.7 / 0.1.6(对应 mermaid 11.4.x)——菱形交点修复

  • 0.1.7:更新处理交点时菱形(diamond)形状偏移量计算(fix: Updated offset calculations for diamond shape when handling intersections);
  • 0.1.6:修复菱形形状在 ELK 渲染中的交点计算(fix: Elk rendering of Diamond shape intersections)。

这两次修复针对的是"边裁剪到节点边界"阶段中菱形(决策节点)四边非轴平行导致的裁剪点错误。当前代码中该阶段对应 render.ts 的sanitizeElkEdgePoints/cutter2与 geometry.ts 中的形状交点计算函数(computeNodeIntersectionreplaceEndpoint等)。

3.8 0.1.5(对应 mermaid 11.3.0)

chore: Update render options

渲染选项更新,为后续把 ELK 配置统一进data4Layout.config.elk命名空间做铺垫。

3.9 0.1.4(无对应 mermaid 依赖变更)

chore: fix render types

修复渲染相关的类型定义。

3.10 0.1.3(对应 mermaid 11.1.0)——默认配置更新 + 破环策略暴露

fix: Updates to the default elk configuration feat: exposing cycleBreakingStrategy to the configuration so that it can be modified using the configuration.

  • 更新了 ELK 的默认 layoutOptions(即今天createRootElkGraph中那组'elk.layered.*'默认项的雏形:unnecessaryBendpoints: truemergeHierarchyEdges: truemultiEdge.improveCuts等);
  • 新增elk.cycleBreakingStrategy配置,允许用户指定 layered 布局的破环策略(如GREEDY_MODEL_ORDER/MODEL_ORDER),接线点在 render.ts:
'elk.layered.cycleBreaking.strategy': data4Layout.config.elk?.cycleBreakingStrategy, // 源码注释中还列出了候选值: // 'elk.layered.cycleBreaking.strategy': 'GREEDY_MODEL_ORDER', // 'elk.layered.cycleBreaking.strategy': 'MODEL_ORDER',

3.11 0.1.2 / 0.1.1(对应 mermaid 11.0.2)

  • 0.1.2:Fix type file path(修复类型文件路径);
  • 0.1.1:fix: Types path(修复types指向)。

这两次都是打包/类型声明路径修正,标志着该包自 mermaid 11.0 起以独立 workspace 包形式对外发布。

四、elk.* 配置参数全表(结合源码解析)

综合 CHANGELOG 与 defaultConfig.ts、render.ts,elk配置命名空间下可写参数及其实时接线位置如下:

配置项默认值引入版本ELK 选项接线位置作用
mergeEdgesfalse11.0 前已存在,0.2.2 修复子图传播根图elk.layered.mergeEdges+ 子图同选项合并同一起止的多条边为一条,减少视觉冗余
nodePlacementStrategy'BRANDES_KOEPF'0.1.5 前后根图与子图nodePlacement.strategy节点坐标放置策略
nodePlacementAlignment'NONE'0.2.3根图与子图elk.layered.nodePlacement.bk.fixedAlignmentBK 策略下的固定对齐(如FIRST/LAST等),控制节点在层内贴边排列
forceNodeModelOrderfalse0.1.9elk.layered.crossingMinimization.forceNodeModelOrder强制按声明顺序减少交叉(牺牲交叉数最优性)
considerModelOrder'NODES_AND_EDGES'0.1.9elk.layered.considerModelOrder.strategy破交叉时"强烈参考"声明顺序的程度(NODES_AND_EDGES/NODES/EDGES/NONE
cycleBreakingStrategy由 JSON Schema 提供0.1.3elk.layered.cycleBreaking.strategy破环(回边)策略,影响递归图的阅读方向
keepEntryNodeOnTopfalse0.2.3命中节点上写elk.layered.layering.layerConstraint: 'FIRST'把递归流入口节点钉在首层

配置通过 frontmatterconfig: elk: {...}mermaid.initialize({ elk: {...} })写入,渲染时整体挂在data4Layout.config.elk上被 render.ts 读取;schema 定义见 config.schema.yaml(elk节点自 L118 起),类型见 config.type.ts。

4.1keepEntryNodeOnTop的算法细节

这是 0.2.3 的核心特性,其实现值得展开。elk.layered必须先破环才能给节点分层,而其默认破环启发式是纯度数的,没有"入口"概念——于是递归流程里第一个声明的节点可能被排到布局中部,阅读顺序被打乱。

render.ts 的findCyclicEntryNodes算法:

  1. parentId把节点分组(即按容器/子图隔离),组内保持声明顺序;
  2. 只用容器内部边、忽略自环,统计每个节点的入度,并用无向邻接表求出弱连通分量(迭代 DFS);
  3. 若某分量中不存在入度为 0 的节点,则该分量必然含环(无环有向图的每个弱连通分量必有源节点),于是提名该分量中声明顺序最靠前的节点作为入口;
  4. 有源的分量(即无环部分)不做任何提名,布局保持原样——这保证了无环图零影响。

随后applyCyclicEntryConstraint(render.ts)只在keepEntryNodeOnTop为真时对命中节点写约束:

if (!data4Layout.config.elk?.keepEntryNodeOnTop) { return; // 默认关闭,现有 ELK 图不受影响 } // ... elkNode.layoutOptions = { ...elkNode.layoutOptions, 'elk.layered.layering.layerConstraint': 'FIRST', };

五、渲染管线:从 LayoutData 到 SVG 坐标

render.ts 用主包提供的工厂函数装配出完整渲染器:

export const render = createCommonLayoutRenderer<ElkLayoutResult, ElkPreparedLayout>({ prepareLayout: prepareLayoutForElk, runLayoutCore: runElkLayoutCore, paintOptions: { skipIntersect: true }, // 交点已由 ELK + 自研裁剪处理 });

完整调用链(buildElkGraphFromLayoutData,render.ts):

  1. createRootElkGraph:构建根 ELK 图,写入elk.algorithm(由加载器注入,如elk.layered/elk.stress)、elk.direction(经dir2ElkDirectionTB/TDDOWNLRRIGHTRLLEFTBTUP)及第四节所列全部根级 layoutOptions;
  2. addSubGraphs:按parentId建立父子查找表parentLookupDb(供跨层级边与坐标偏移使用);
  3. addVertices/addVertex:递归把节点加入 ELK 图;子图节点挂labelData(标签实测宽高),普通节点挂width/height
  4. addEdgesToElkGraph:把 mermaid 边转换为 ELK 边,边标签带edgeLabels.placement: CENTER
  5. configureSubgraphNodes:为每个子图写buildSubgraphLayoutOptions(子图可有自己的direlk.algorithm,并设elk.hierarchyHandling: 'SEPARATE_CHILDREN'独立布局子图内部),并删除子图自身宽高让其由内容撑开;
  6. configureCrossHierarchyEdges:对父子不同的两端,用 find-common-ancestor.ts 找最近公共祖先,并沿祖先链设置elk.hierarchyHandling: 'INCLUDE_CHILDREN',使跨子图边可以正确穿越层级容器;
  7. applyCyclicEntryConstraint:按 4.1 所述钉住递归入口;
  8. runElkLayout:调用new ELK().layout(graph)(elkjs 打包版),并支持 dev 构建下通过globalThis.__mermaidProfilerlayoutCore单独计时;出错时打印完整 ELK 图便于排查;
  9. applyElkLayoutResult
    • applyElkNodePositions递归回填坐标——子图坐标是相对父容器的,需累加relX/relY偏移得到全局posX/posY,节点中心取x + width/2
    • applyElkEdgeLayout取 ELK 返回的sections[0](起点/折点/终点),叠加calcOffset(公共祖先偏移)换算到全局坐标,再经sanitizeElkEdgePointscutter2裁剪到节点边界、组端点贴边检测、去重、无效点兜底)与ensureEndMarkerSegmentLength(保证箭头头段长度 ≥ 8px)输出最终points,并写curve = 'rounded'(0.2.1 行为);
  10. orderNodesForElkPaint:绘制顺序上,组(子图)先于普通节点,组之间按嵌套深度升序,保证子图背景先画、节点后画。

六、验证方式:e2e 用例目录

仓库为 ELK 布局维护了完整的 e2e 快照用例,可用于对照验证各版本行为:

  • 流程图 ELK 用例:e2e/diagrams/flowchart/elk/(56 个.mmd,覆盖嵌套子图、样式表达式、边裁剪2824-elk-clipping-of-edges.mmd、方向继承2050-elk-handling-of-different-rendering-direction-in-subgraphs.mmd等场景);
  • 类图 ELK 用例:e2e/diagrams/class-diagram/elk/(60 个.mmd);
  • 快照测试入口:e2e/helpers/mmd-snapshots.ts、e2e/helpers/mmd-snapshots.spec.ts;
  • 包自身的单元测试:packages/mermaid-layout-elk/src/tests/render.spec.ts(针对布局核心函数)、geometry.spec.ts(边界交点几何)。

七、实践要点小结

  1. 接入前提:ELK 不随 mermaid 主包默认提供,宿主环境必须npm install @mermaid-js/layout-elkregisterLayoutLoaders,否则layout: elk不生效;peer 要求mermaid ^11.0.2
  2. 默认行为(0.2.3 起)elk别名 =elk.layered分层布局;边默认rounded圆角折线;节点顺序"强烈参考"声明序而非强制;mergeEdgeskeepEntryNodeOnTop默认关闭,属纯 opt-in增强,不改变存量图。
  3. 调参路径:全局elk.*配置 → 根图 layoutOptions;子图内部则经buildSubgraphLayoutOptions传播(0.2.2 修复后mergeEdges对子图内边同样生效)。递归流程阅读顺序问题用keepEntryNodeOnTop: true解决;层内排列不齐可用nodePlacementAlignment/nodePlacementStrategy调整;回边方向不佳可用cycleBreakingStrategy换破环策略。
  4. 可追溯性:每条行为都能对上 CHANGELOG 条目与源码位置(CHANGELOG、render.ts、defaultConfig.ts),配合 e2e.mmd快照即可复现验证。

【免费下载链接】mermaidGeneration of diagrams like flowcharts or sequence diagrams from text in a similar manner as markdown项目地址: https://gitcode.com/GitHub_Trending/me/mermaid

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

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

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

立即咨询