让 Hugo 博客支持 Mermaid 图表

之前写过一篇文章, 讲怎么用 Shortcodes 给博客加 ECharts 图表. 但日常写技术文章更常需要的是流程图、时序图这类"文本即图形"的图. Mermaid 正好干这个: 用几行文本描述一张图, 不用拖画布, 不用贴截图.

而且现在 Hugo 有比 Shortcodes 更优雅的方案——代码块渲染钩子 (code block render hook). 从 Hugo v0.123 开始, 可以自定义代码块的渲染, 拦截 mermaid 语言的围栏代码块, 在构建时直接输出成可渲染的图形容器.

一、创建渲染钩子

在项目根目录创建 layouts/_default/_markup/render-codeblock.html:

{{ $lang := .Type | default "plain" }}
{{ if eq $lang "mermaid" }}
<pre class="mermaid" role="img" aria-label="Mermaid diagram">{{ .Inner }}</pre>
{{ if not (.Page.Store.Get "mermaidLoaded") }}
{{ .Page.Store.Set "mermaidLoaded" true }}
<script type="module">
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs';
mermaid.initialize({
  startOnLoad: false,
  theme: 'base',
  themeVariables: {
    background: '#1d1f21',
    primaryColor: '#282c34',
    primaryBorderColor: '#3e4147',
    primaryTextColor: '#c9cacc',
    lineColor: '#5a5f66',
    edgeLabelBackground: '#1d1f21'
  }
});
mermaid.run({ querySelector: 'pre.mermaid' });
</script>
{{ end }}
{{ else }}
{{ highlight .Inner .Type }}
{{ end }}

这个模板做的事很简单:

  • 当代码块语言是 mermaid 时, 把内容包进 <pre class="mermaid">, 同时引入 mermaid v11 (ESM 版) 并调用 mermaid.run() 在浏览器端渲染成 SVG
  • .Page.Store 做"每页只输出一次 script"的守卫, 页面里有多个图时 mermaid 也只加载一份
  • <pre> 上加 role="img"aria-label, 让屏幕阅读器能识别出这是图片
  • themeVariables 把配色调成和本站暗色背景一致, 用的时候按自己的主题配色替换即可
  • 其他语言的代码块继续走 Hugo 内置的 highlight 高亮逻辑, 互不影响

注意: Hugo 渲染代码块时已经对 .Inner 做了 HTML 转义, 模板里直接输出即可, 浏览器端的 textContent 会还原成原始文本. 我一开始在这里又手动转义了一次, 结果 mermaid 解析到 &lt; 这类字面实体而报 Syntax error in text, 后来就只直接输出, 不再手动转义.

二、在文章里用

写 Markdown 时, 直接写 mermaid 围栏代码块即可:

```mermaid
graph TB
    A[开始] --> B{支持吗?}
    B -- 支持 --> C[完美渲染]
    B -- 不支持 --> D[查看控制台]
```

不需要任何 Shortcodes, 不需要在 front matter 里声明, 构建时自动生效.

三、实测效果

下面几个图如果都显示为真实的图形 (而不是代码文本), 说明博客的 Mermaid 支持已经生效.

流程图 flowchart

时序图 sequenceDiagram

类图 classDiagram

甘特图 gantt

饼图 pie

四、注意事项

  • 需要 Hugo v0.123+ 才能使用代码块渲染钩子
  • mermaid 通过 CDN 按需加载, 只有页面里有 mermaid 代码块才会引入, 不影响其他页面速度
  • 图形由浏览器端渲染成 SVG, 搜索爬虫看到的是可读的原始文本, SEO 友好

五、如何防止以后把图表写错

图表是浏览器端渲染的, 写错了构建不会报错, 只会让页面弹出一行 Syntax error in text. 这个坑我踩过一次: 我在渲染钩子里对 .Inner 又做了一次 htmlEscape, 结果 Hugo 已经转义过的 --> 变成了 --&gt;, 浏览器端的 textContent 还原出字面 &gt;, mermaid 直接解析失败.

为了以后不再写错, 我把校验做成了 skill——mermaid-lint. 它用真实 mermaid 解析器逐个校验 content/ 下所有文章里的 mermaid 块, 同时静态检查两类问题: 内容里出现字面 HTML 实体 (双重转义), 首行不是合法图类型. 核心是 scripts/lint-mermaid.mjs, 跑法:

node scripts/lint-mermaid.mjs

全部通过会输出全绿, 有图写错会指出是哪篇文章第几个块、错在哪, 退出码非 0, 正好卡在 CI 里.

写图的原则就一条: Markdown 源码里写原始字符 (-->"label"), 永远不要写 &gt;&#34; 这类 HTML 实体.


如果上面的流程图、时序图、类图、甘特图和饼图都渲染出了真实图形, 那说明一切正常. 我给博客加上这套能力时, 复制的就是上面那个模板文件.

文章链接:

https://time-friend.com/zh/archive/make-your-hugo-blog-support-mermaid-diagrams/

# 相关文章推荐