首页
/ Mermaid @mermaid-js/examples 实战指南:构建、验证与消费官方示例图库

Mermaid @mermaid-js/examples 实战指南:构建、验证与消费官方示例图库

2026-09-06 16:15:21作者:晏闻田Solitary

本文围绕 mermaid 仓库中的 packages/examples/README.md 展开,讲解 @mermaid-js/examples 包的定位、数据结构与扩展方式:如何为某个图表类型编写并注册示例图、测试门禁如何保证每条示例都能被正确解析,以及第三方工具(如 mermaid.live)如何消费这个示例库获取每种图表的默认样例。读完本文,你可以独立完成新增一个图表示例、理解示例数据的 id/name/examples 三元结构,并复用官方示例消费逻辑为自己的编辑器或文档站点生成样板代码。

1. 包的定位:示例图不是文档,而是可安装的运行时数据

@mermaid-js/examples 是一个独立的 npm 包,它把 mermaid 支持的各种图表类型的示例源码(flowchart、sequence、C4、gantt、state 等)组织成一份结构化的 TypeScript 数据,供 mermaid.live 等工具在用户新建图表时提供"开箱即用"的起始代码。

package.json 可以看到它的关键特征:

  • 包名 @mermaid-js/examples,当前版本 1.4.0,MIT 协议,"type": "module" 的纯 ESM 包;
  • 入口为编译产物:"module": "./dist/mermaid-examples.core.mjs",类型声明在 ./dist/index.d.tsexports 只暴露 . 一个子路径,即整个包只导出示例数据,没有附带运行时代码依赖(dependencies 为空对象);
  • 唯一的运行时协作对象是 mermaid 本身,且仅以 "mermaid": "workspace:*" 的形式出现在 devDependencies 中——说明示例包在生产侧是"纯数据",只有在仓库内部做测试时才依赖 mermaid 来校验示例可解析性。

这意味着任何宿主应用引入该包后,拿到的只是一份经过类型约束的元数据数组,渲染职责完全由宿主侧的 mermaid 实例承担。

2. 数据结构:Example 与 DiagramMetadata 两层模型

理解整个包的起点是 types.ts,它定义了只有两个接口:

export interface Example {
  title: string;
  code: string;
  isDefault?: boolean;
}

export interface DiagramMetadata {
  id: string;
  name: string;
  description: string;
  examples: Example[];
}

各字段含义与约定如下:

字段 层级 含义与约束
id DiagramMetadata 与 mermaid 内部注册的图表 id 对应(如 flowchart-v2c4usecase),是测试用例做 diagramData.find((d) => d.id === diagram.id) 匹配的键
name DiagramMetadata 面向用户的图表名称,如 FlowchartC4 Diagram
description DiagramMetadata 一句话描述该图表的用途,宿主可用它生成下拉菜单说明文字
title Example 单条示例的展示标题
code Example 完整的 mermaid 图表源码字符串,可直接丢给 mermaid.render / mermaid.parse
isDefault Example 可选布尔值,标记该图表的"默认示例";约定每个图表有且仅有一条为 true

所有示例文件都以 export default { ... } satisfies DiagramMetadata; 结尾,satisfies 关键字保证在获得类型检查的同时保留字面量推断,任何字段拼写错误都会在建构时直接报错。

3. 中央注册表:index.ts 中的 diagramData 数组

index.ts 是唯一的聚合点。它的模式非常直接:

  1. 逐文件 import 每个图表的默认导出的元数据对象,例如 import flowChart from './examples/flowchart.js'import c4 from './examples/c4.js'
  2. 将这些对象按顺序平铺进一个导出的 diagramData: DiagramMetadata[] 数组。

当前注册表收录了 33 个条目,覆盖 flowchart、c4、ishikawa、kanban、class、sequence、pie、user-journey、mindmap、requirement、radar、state、er、git、architecture、xychart、sankey、gantt、timeline、quadrant-chart、packet、block、treemap、usecase、eventmodeling、venn、tree-view、wardley、cynefin,以及 railroad 的四种变体(通用/EBNF/ABNF/PEG,分别对应 railroad.tsrailroad-ebnf.tsrailroad-abnf.tsrailroad-peg.ts)。

README 中给出的扩展流程(packages/examples/README.md)对应到源码就是两步:

  • 复制并修改示例文件:以 flowchart.ts 为模板,新建 packages/examples/src/examples/<你的图表>.ts,按 DiagramMetadata 结构填写;
  • 在 index.ts 中注册:添加一行 import,并把对象追加进 diagramData 数组。

README 同时强调了内容策略:"每种图表至少应有一条示例,且必须标记为默认(isDefault: true);建议再添加更多示例以展示该图表的不同特性。" 以 flowchart 为例,flowchart.ts 实际提供了 4 条示例,逐层递进地展示能力面:

export default {
  id: 'flowchart-v2',
  name: 'Flowchart',
  description: 'Visualize flowcharts and directed graphs',
  examples: [
    {
      title: 'Basic Flowchart',
      isDefault: true,
      code: `flowchart TD
    A[Christmas] -->|Get money| B(Go shopping)
    B --> C{Let me think}
    C -->|One| D[Laptop]
    C -->|Two| E[iPhone]
    C -->|Three| F[fa:fa-car Car]`,
    },
    {
      title: 'Online Checkout Flow',
      code: `flowchart TD
    Start([Visit online store]) --> Browse[Browse products]
    ...
    style Start fill:#e8f5e9,stroke:#43a047`,
    },
    // 还有 'CI/CD Pipeline with Subgraphs'(子图与跨子图连线)、
    // 'Expanded Node Shapes'(@{ shape: ... } 扩展节点形状)等
  ],
} satisfies DiagramMetadata;

这个分层写法值得借鉴:第一条默认示例保持最小、稳定(便于宿主做"新建图表"占位);后续示例则分别演示 style 指令、subgraph 跨图连线、@{ shape: docs, ... } 等进阶语法,形成一份内嵌的能力展示目录。c4.ts 同样遵循该模式,默认示例是完整的 Internet Banking C4Context(含 Enterprise_Boundary 嵌套、Person / System_Ext / SystemQueue 等节点类型与 BiRel/Rel 关系),第二条则换成 C4Container 视角。

4. 质量门禁:测试如何保证示例库"永远可渲染"

示例库最大的风险是"文档腐化"——mermaid 语法演进后示例悄悄失效。example.spec.ts 用三层测试封死了这个风险:

  1. 覆盖性检查:调用 mermaid.getRegisteredDiagramsMetadata() 取出运行时注册的全部图表,剔除明确的跳过列表(errorinfo 等无示例图表,以及被 v2 版本覆盖的旧 id 如 graphflowchartclass),断言每个注册图表都能在 diagramData 中找到同 id 的条目、示例数大于 0,且 data.examples.filter((e) => e.isDefault).length 恰好为 1——即"每种图表必须有示例、且默认示例唯一"是硬约束;
  2. 全量可解析性检查:遍历 diagramData 中每一条 example.code,断言 mermaid.parse(example.code) 成功。任何一条示例语法写坏,对应测试会以 Example "<title>" of <id> does not parse 明确指向责任条目;
  3. 抽样真实渲染检查:对 usecase 的每条示例额外执行 mermaid.render,断言产物中存在 <svg> 且包含 [data-usecase-kind] 节点,验证的不只是"能解析",还是"能渲染出预期结构"。

测试还做了环境适配:mock SVGElement.prototype.getBBox / getComputedTextLength 以支持无头浏览器环境下的渲染测量。从源码结构看,这套门禁意味着上游 PR 只要改动示例语法,就会在 CI 中被精确到"哪条示例的哪个标题"级别的报错拦截。

5. 宿主侧消费:mermaid.live 的用法

安装与最小用法在 README 中给出:

pnpm add @mermaid-js/examples

下面是 README 提供的、源自 mermaid.live 的完整消费示例:为每种图表类型取回其默认示例代码,并按"去掉 Diagram/Chart/Graph 后缀"的规则生成 Record<string, string> 映射:

import { diagramData } from '@mermaid-js/examples';

type DiagramDefinition = (typeof diagramData)[number];

const isValidDiagram = (diagram: DiagramDefinition): diagram is Required<DiagramDefinition> => {
  return Boolean(diagram.name && diagram.examples && diagram.examples.length > 0);
};

export const getSampleDiagrams = () => {
  const diagrams = diagramData
    .filter((d) => isValidDiagram(d))
    .map(({ examples, ...rest }) => ({
      ...rest,
      example: examples?.filter(({ isDefault }) => isDefault)[0],
    }));
  const examples: Record<string, string> = {};
  for (const diagram of diagrams) {
    examples[diagram.name.replace(/ (Diagram|Chart|Graph)/, '')] = diagram.example.code;
  }
  return examples;
};

这段代码里有三个值得注意的工程细节:

  • isValidDiagram 用类型谓词把"可能不完整"的条目过滤掉,后续代码无需再写空值判断;
  • .map(({ examples, ...rest }) => ...) 通过解构把 examples 数组摘除,只保留选中的默认示例,控制下发到前端的数据体积;
  • 键名规范化 diagram.name.replace(/ (Diagram|Chart|Graph)/, '') 把 "C4 Diagram"、"Quadrant Chart"、"Git Graph" 之类的不统一命名收敛为 "C4"、"Quadrant"、"Git",这是把展示层 name 转回机器可读键的实用技巧。

如果你的宿主应用需要的是"完整示例列表"而非仅默认示例(例如构建文档站、图表选型页),直接遍历 diagramDataexamples 数组即可,title 用作展示名、code 填入编辑器、isDefault 决定初始高亮。

6. 适用前提与限制

  • 该包只发布 dist 产物("files": ["dist"]),消费方拿到的是编译后的 .mjs.d.ts,示例源码本身在 src/examples/ 目录下,需要修改示例时应回到源码仓库提交 PR,而非本地改写 dist;
  • diagramDataid 必须与 mermaid 当前版本的图表注册 id 对齐(如 flowchart 的 id 是 flowchart-v2 而非 graph)。从 CHANGELOG.md 可以看到,每当 mermaid 新增图表类型(Ishikawa、TreeView 等),examples 包都会跟随发布一个 minor 版本补齐示例,这是两个包版本联动的原因;
  • 1.3.0 / 1.4.0 两个版本的重点是"为每种图表补充贴近真实场景、突出该图表强项的示例",因此示例内容本身在持续演化,消费方不应把具体 code 字符串当作长期不变的常量缓存,建议每次依赖升级后重新生成样板数据。

7. 小结:扩展一个图表示例的完整清单

结合 README 与源码,新增一个图表示例的完整流程可以归纳为:

  1. 复制 flowchart.tspackages/examples/src/examples/<name>.ts,按 DiagramMetadata 结构填写 id(与 mermaid 注册 id 一致)、namedescription 与至少一条 isDefault: true 的示例;
  2. index.ts 中添加 import 并追加进 diagramData 数组;
  3. 依赖 example.spec.ts 的三层门禁自动验证:id 覆盖、默认示例唯一、全部示例可解析(必要时补充渲染级断言);
  4. 宿主侧通过 pnpm add @mermaid-js/examples 引入,按第 5 节的 getSampleDiagrams 模式取数即可。

这样,示例库既是用户的"入门样板",也是 mermaid 各图表语法的活体回归测试集——数据只有一份,文档、编辑器与测试共享同一来源。

登录后查看全文
热门项目推荐
相关项目推荐