首页
/ Mermaid Directives 深度解析:用 %%{init}%% 指令在图形代码内覆盖主题与图表配置的完整机制

Mermaid Directives 深度解析:用 %%{init}%% 指令在图形代码内覆盖主题与图表配置的完整机制

2026-09-06 16:42:50作者:羿妍玫Ivan

本文以 Mermaid 官方的 Directives 文档 为主体,系统讲解 %%{init: ...}%% 指令的语法结构、init/initialize 的合并规则、通用与图表专属两类配置项的写法(theme、fontFamily、logLevel、flowchart、sequence),并结合当前仓库源码还原指令的完整生命周期:从正则匹配、预处理提取、安全清洗(sanitize)到与站点配置、frontmatter 的合并优先级,帮助你在编写图表时精准控制单图外观,并理解哪些配置项因安全原因不可被指令覆盖。

一、Directives 的定位:写在图里的配置覆盖层

Directives(指令)让图表作者能够在渲染之前、直接以文本形式改变图表外观。它的价值在于:

  • 书写时可用:指令就写在图表文本中,和图形定义一起流转,无需额外的 JS 调用;
  • 叠加在默认配置之上:指令修改的是全局默认配置和图表专属配置,属于“单图级”(individual level)覆盖;
  • 部分配置不可覆盖:出于安全原因,某些配置项不允许通过指令修改;同时你还有权定义“允许图表作者覆盖的配置集合”。

需要注意的是,官方文档开头明确标注了弃用警告

Directives 从 v10.5.0 起已弃用(deprecated),建议改用 frontmatter 中的 config 键来传递配置,详见 Configuration 文档

也就是说,在当前仓库中 Directives 仍被完整实现和测试(后文会给出源码证据),但新项目中推荐的新写法是 YAML frontmatter:

---
config:
  theme: forest
---
graph LR
A-->B

本文仍按原文档脉络完整讲解 Directives 本身,并补充两条机制在源码中如何并存的事实。

二、两类可被指令覆盖的配置

Mermaid 支持两类可被指令覆盖的配置(原文档“Types of Directives options”一节):

1. General/Top Level configurations(通用/顶层配置)

作用于所有图表的通用配置,其中最常通过指令使用的包括:

  • theme
  • fontFamily
  • logLevel
  • securityLevel
  • startOnLoad
  • secure

2. Diagram-specific configurations(图表专属配置)

只作用于特定图表类型的配置。例如 mirrorActorsSequenceDiagram 专属配置,控制参与者是否镜像显示,因此它只能出现在 sequence 这一层。

原文档提示:并非所有配置项都在文档中列全,完整列表应参考源码中的 packages/mermaid/src/defaultConfig.ts(原文档给出的外部 GitHub 链接在此统一替换为仓库内路径)。

三、指令的声明语法:%% {directive_text} %%

一条指令始终以两个 % 符号开头和结尾,即 %% {directive_text} %%。指令文本的结构是一个以 init 为根的嵌套键值对映射(JSON 对象):

  • 顶层:放通用(General)配置;
  • 深一层、以图表类型为 key:放该图表的专属配置。

完整结构示例(原文档原样保留):

%%{
  init: {
    "theme": "dark",
    "fontFamily": "monospace",
    "logLevel": "info",
    "htmlLabels": true,
    "flowchart": {
      "curve": "linear"
    },
    "sequence": {
      "mirrorActors": true
    }
  }
}%%

也可以写在一行内:

%%{init: { **insert configuration options here** } }%%

例如:

%%{init: { "sequence": { "mirrorActors":false }}}%%

注意(原文档 Notes):作为参数传入的 JSON 对象必须是合法的键值对,且键要加引号,否则会被忽略;合法的键值对可参考 config 文档。

源码印证:指令如何被识别

diagram-api/regexes.ts 中定义了识别指令的正则:

export const directiveRegex =
  /%{2}{\s*(?:(\w+)\s*:|(\w+))\s*(?:(\w+)|((?:(?!}%{2}).|\r?\n)*))?\s*(?:}%{2})?/gi;

它匹配 %%{ ... %%} 的成对结构,并捕获形如 init:initialize: 的键名;同文件还定义了 frontMatterRegex(Jekyll 风格 frontmatter)与 anyCommentRegex,这与下文“指令与 frontmatter 合并”的源码路径一一对应。

四、指令的解析与合并规则

initinitialize 等价且会被合并

initinitialize 都可作为初始化指令的键,且解析后会被归并为同一条指令。原文档示例:

%%{init: { 'logLevel': 'debug', 'theme': 'forest' } }%%
%%{initialize: { 'logLevel': 'fatal', "theme":'dark', 'startOnLoad': true } }%%
...

解析后会生成一个单一的 %%init%% JSON 对象,合并两条指令,并对重复的 logLevel 取最后一次出现的值

{
  "logLevel": "fatal",
  "theme": "dark",
  "startOnLoad": true
}

该对象随后会交给 mermaid.initialize(...) 用于渲染。

最小示例:logLevel 与 theme

%%{init: { 'logLevel': 'debug', 'theme': 'dark' } }%%
graph LR
A-->B

这条指令声明将 logLevel 设为 debugtheme 设为 dark,直接改变渲染结果的外观。

五、典型配置项逐例讲解

以下四个小节完整继承原文档“Directive Examples”中的示例与参数说明。

5.1 通过指令修改 theme

theme 改为 forest

%%{init: { "theme": "forest" } }%%

可选值:defaultbasedarkforestneutral;默认值是 default

%%{init: { "theme": "forest" } }%%
graph TD
A(Forest) --> B[/Another/]
A --> C[End]
  subgraph section
  B
  C
  end

源码侧,theme 的取值与内置主题一一对应:config.ts 中的 updateCurrentConfig 会检查 sumOfDirectives.theme in theme,命中后调用 theme[cfg.theme].getThemeVariables(...) 生成该主题的变量集,再合并进指令里自定义的 themeVariables——这就是“指令改主题”在底层的落点。

5.2 通过指令修改 fontFamily

%%{init: { "fontFamily": "Trebuchet MS, Verdana, Arial, Sans-Serif" } }%%

%%{init: { "fontFamily": "Trebuchet MS, Verdana, Arial, Sans-Serif" } }%%
graph TD
A(Forest) --> B[/Another/]
A --> C[End]
  subgraph section
  B
  C
  end

一个有意思的源码细节:addDirective 中专门处理了 fontFamily——如果指令带了 fontFamily 却没有 themeVariables,它会自动把 fontFamily 复制进 themeVariables.fontFamily(见 config.tsaddDirective 函数)。这解释了为什么“顶层写 fontFamily”能真正影响字体渲染,而不必手工写主题变量。

5.3 通过指令修改 logLevel

%%{init: { "logLevel": 2 } }%%

logLevel 的取值(原文档列表完整保留):

  • 1debug
  • 2info
  • 3warn
  • 4error
  • 5only fatal errors

默认值是 5

%%{init: { "logLevel": 2 } }%%
graph TD
A(Forest) --> B[/Another/]
A --> C[End]
  subgraph section
  B
  C
  end

5.4 通过指令修改 flowchart 配置

常用 flowchart 配置(原文档列表):

  • htmlLabels:已弃用,建议改在根级别设置;
  • curvelinear/curve
  • diagramPadding:number;
  • useMaxWidth:number。

完整列表参见 packages/mermaid/src/defaultConfig.ts

只覆盖 flowchart 配置(不碰通用配置)的写法:

%%{init: { "htmlLabels": true, "flowchart": { "curve": "linear" } } }%%

Warning(原文档原文)Deprecated: flowchart.htmlLabels 自 v11.12.3 起弃用。请改用全局 htmlLabels 配置。例如不要写 "flowchart": { "htmlLabels": true },而是把 "htmlLabels": true 放在顶层。

%%{init: { "flowchart": { "htmlLabels": true, "curve": "linear" } } }%%
graph TD
A(Forest) --> B[/Another/]
A --> C[End]
  subgraph section
  B
  C
  end

这个弃用规则在源码中有对应实现:config.tsgetEffectiveHtmlLabels 函数按 根级 htmlLabels → flowchart.htmlLabels → 默认 true 的优先级取值,且一旦检测到 config.flowchart.htmlLabels 已设置,就会通过 issueWarning 打印 flowchart.htmlLabels is deprecated. Please use global htmlLabels instead. 的警告。

5.5 通过指令修改 Sequence 图配置

常用 sequence 配置(原文档列表完整保留):

  • width:number
  • height:number
  • messageAlignleftcenterright
  • mirrorActors:boolean
  • useMaxWidth:boolean
  • rightAngles:boolean
  • showSequenceNumbers:boolean
  • wrap:boolean

先看默认(wrap 默认为 false):

sequenceDiagram

Alice->Bob: Hello Bob, how are you?
Bob->Alice: Fine, how did your mother like the book I suggested? And did you catch the new book about alien invasion?
Alice->Bob: Good.
Bob->Alice: Cool

启用 wrap(并把 width 设为 300):

%%{init: { "sequence": { "wrap": true} } }%%

%%{init: { "sequence": { "wrap": true, "width":300 } } }%%
sequenceDiagram
Alice->Bob: Hello Bob, how are you?
Bob->Alice: Fine, how did your mother like the book I suggested? And did you catch the new book about alien invasion?
Alice->Bob: Good.
Bob->Alice: Cool

应用该片段后,长消息会在参与者之间换行显示,图表整体宽度被约束在 300px 附近。

六、源码纵深:指令从文本到生效配置的完整链路

结合当前仓库源码,Directives 的完整生命周期如下(文件路径均可在当前仓库中查看):

6.1 预处理阶段:提取、合并、删除

preprocess.ts 中的 processDirectives 是入口:

  1. utils.detectInit(code) 检测 %%init%% 指令并解析为对象(见 utils.tsdetectInitremoveDirectives);
  2. 同时检测 wrap 类指令(%%{wrap: true}%%),若存在则把 initDirective.wrap 置为 true
  3. 调用 removeDirectives(code) 把指令文本从源码中删除,避免指令内容进入图形解析器;
  4. 最后由 cleanAndMerge(frontmatter.config, directive)frontmatter 的 config 与 directive 结果合并——这正是“v10.5.0 后推荐 frontmatter、但 directives 仍然可用”两条机制在源码层的会合点。

6.2 配置合并阶段:addDirective 与优先级

mermaidAPI.ts 在解析出图表元数据后调用 configApi.addDirective(processed.config ?? {}),注释明确写着 “Important that we do not create the diagram until after the directives have been included”——即先注入指令,再创建图表对象,保证图表拿到的配置已包含指令覆盖。

config.ts 中的 updateCurrentConfig 给出优先级事实(从源码结构看):

defaultConfig < siteConfig(来自 mermaid.initialize) < 按顺序累积的 directives
  • siteConfig 为基底深拷贝(assignWithDepth);
  • 逐条 sanitize(d) 后依次 assignWithDepthsumOfDirectives——多条指令中后者覆盖前者,与第四节 logLevel: fatal 覆盖 debug 的行为一致;
  • 再把 sumOfDirectives 叠加到基底上,形成 currentConfig

reset() 会清空 directives 并把配置重置回 siteConfig,保证渲染下一张图时不会串味。

6.3 安全边界:两层 sanitize

文档说“出于安全原因,部分配置不可用指令修改”,源码里这是两层具体实现:

第一层:secure 键保护config.tssanitize

  • securesiteConfig.secure 数组中列出的键,若在指令中出现会被直接删除(日志记录 “Denied attempt to modify a secure key”);
  • 删除所有以 __ 开头的键(防原型污染);
  • 删除任何包含 <>url(data: 的字符串值(防 XSS / base64 内嵌 SVG 脚本)。

第二层:白名单 + 值校验utils/sanitizeDirective.tssanitizeDirective

  • 键白名单:不在 defaultConfig.ts 导出的 configKeys 中的键一律删除——这就是“不是所有配置都能被指令改变”的真正原因,可用键集合严格等于默认配置暴露的键;
  • 危险键模式startsWith('__')、含 proto、含 constr 的键被删除;
  • 字典型配置的值校验nodeColors(sankey 的 CSS 颜色)、filenameIcons/extensionIcons(treeView 的 iconify 图标引用)按 DICTIONARY_CONFIG_PATTERNS 中的正则校验值,不合规的条目被删除;
  • CSS 相关键themeCSSfontFamilyaltFontFamily)会经 sanitizeCss 做花括号配平检查,不平衡时替换为 { /* ERROR: Unbalanced CSS */ }
  • themeVariables 值校验:值需匹配 /^[\d "#%(),.;A-Za-z]+$/,否则清空为 ''

6.4 E2E 佐证

仓库的端到端测试套件里存在专门的 conf-and-directives 用例目录,其中 settings-from-initialize-nodes-should-be-green.mmd 验证 %%initialize%% 指令确实能改变渲染结果(节点着色),与文档中“init/initialize 等价”的说明互为印证。

七、实践建议与要点回顾

  1. 单图覆盖用指令,全局覆盖用 mermaid.initialize:指令的作用域是“当前图”,适合把主题、字体、wrap 等外观决策写在图表文本里随文档流转;
  2. 新代码优先 frontmatter:由于 Directives 自 v10.5.0 起标记弃用,frontmatter 的 config 键是官方推荐写法;当前版本中两者并存且最终在 cleanAndMerge 处汇合;
  3. 重复键取最后一个:多条 init/initialize 合并时后值覆盖前值,写多条指令时注意顺序;
  4. 键名必须合法:JSON 键要加引号,否则被忽略;且键必须存在于 defaultConfig.ts 的键集合中,否则被 sanitize 静默删除;
  5. 安全键改不了secure、原型污染模式、含标签或 data: URL 的字符串都会被清洗,试图通过这些通道注入样式/脚本是无效的;
  6. 弃用项要迁移flowchart.htmlLabels 已弃用(v11.12.3+),改用顶层 htmlLabels,源码会打印对应警告。

核心事实速查表

主题 说明 依据
指令语法 %%{init: {...} }%%init/initialize 等价 docs/config/directives.mddiagram-api/regexes.ts
生效时序 预处理提取 → 删除指令文本 → 先注入配置再建图 preprocess.tsmermaidAPI.ts
合并优先级 defaultConfig < siteConfig < directives(后写覆盖) config.ts updateCurrentConfig
可用键集合 defaultConfig.tsconfigKeys 白名单为准 utils/sanitizeDirective.ts
安全清洗 secure 键、__/proto/constr 键、<>/data: 字符串、CSS 配平、themeVariables 值校验 config.tsutils/sanitizeDirective.ts
推荐写法 frontmatter config 键(directives 自 v10.5.0 弃用) docs/config/directives.mddocs/config/configuration.md
登录后查看全文
热门项目推荐
相关项目推荐