背景
在很久以前,我给我的Blog的主题加了 Hugo 的针对 Mermaid 和 Chart.js shortcodes 的支持。但是之支持迁入代码的方式。 如果文档很复杂,在Markdown里写大段的代码体验观感也不是很好。 而现在很多Markdown生态里是支持通过代码块的方式嵌入的。 同时现在很多插件可以直接按文件后缀编辑文件,然后直接指向文件的形式引用进来,这样可维护性会好很多。
我之前也懒得一个一个组件去研究和何种模式的测试搭配,现在AI很强了,刚好可以让它帮我把这个优化干了。 并且可以让这些插件都支持各种引入方式,确实是省事儿不少。
实测下来写代码还是Claude强,Google Gemini 3 Pro/Flash 也好,GPT 5.2/5.1-Codex-Max也好都会不同层度的瞎写。 Google Gemini 3 Pro写代码感觉比GPT 5.2强一点。但是通用总结,比如让它给我改Markdown的lingt错误Google Gemini 3 Pro比GPT 5.2差很多。
以下就是花了差不多2个小时,使用的Claude Opus 4.5模型,一口气消耗了Github copilot 75个高级Request(Copilot Pro+订阅的5%)。 最后完成了对 Mermaid、Chart.js、Excalidraw、draw.io/diagrams.net 的统一支持各种引用模式的支持。
AI自动生成的测试用例
This page demonstrates all supported methods for rendering diagrams and charts:
- Draw.io/diagrams.net -
.drawiofiles - Mermaid -
.mermaidfiles - Excalidraw -
.excalidrawfiles - Chart.js -
.chart.jsonfiles - ECharts -
.echarts.jsonfiles - Plotly.js -
.plotly.jsonfiles - D2 -
.d2files, rendered to inline SVG at build time - PlantUML -
.puml/.plantumlfiles, rendered to inline SVG at build time
Each format supports 4 usage methods:
- Image syntax:
 - Code block:
```language - Shortcode with src:
{{< type src="file" >}} - Shortcode inline:
{{< type >}}content{{< /type >}}
1. Draw.io/diagrams.net 测试
1.1 图片语法引用 .drawio 文件
下面是通过图片语法引用的 draw.io/diagrams.net 图表:
1.2 代码块方式(内嵌XML)
1.3 Shortcode 引用外部文件
1.4 Shortcode 内联方式
2. Mermaid 图片语法测试
2.1 图片语法引用 .mermaid 文件
下面是通过图片语法引用的 Mermaid 图表:
流程图
2.2 代码块方式
graph LR
A[代码块方式] --> B{工作正常?}
B -->|是| C[蓝色流程图]
B -->|否| D[检查代码]
2.3 Shortcode 引用外部文件
Loading mermaid diagram...
2.4 Shortcode 内联方式
graph TD
A[Shortcode 内联] --> B[Mermaid 图表]
B --> C{正常显示?}
C -->|是| D[成功]
C -->|否| E[检查]
3. Excalidraw 图片语法测试
3.1 图片语法引用 .excalidraw 文件
下面是通过图片语法引用的 Excalidraw 图表:
3.2 Shortcode 引用外部文件
3.3 Shortcode 内联方式
3.4 代码块方式
4. Chart.js 测试
4.1 图片语法引用 .chart.json 文件
下面是通过图片语法引用的 Chart.js 图表:
4.2 Shortcode 引用外部文件
4.3 Shortcode 内联方式
4.4 代码块方式
4.5 代码块方式 (chartjs 语法)
5. ECharts 测试
ECharts 的扩展是增量式的:默认只加载核心包(含所有 2D 图表),需要 3D 时通过 extensions="gl" 追加 echarts-gl。下面分别用 2D 数学函数图 y = sin(x) 和 3D 曲面图 z = sin(sqrt(x^2 + y^2)) 覆盖四种引入方式。
5.1 图片语法引用 .echarts.json(2D 数学函数)
最小包即可渲染:
5.2 代码块方式(2D 数学函数)
5.3 Shortcode 引用外部文件(3D 曲面,gl 扩展)
3D 曲面需要 echarts-gl,通过 extensions="gl" 声明:
5.4 Shortcode 内联方式(3D 曲面,gl 扩展)
5.5 代码块方式(3D 曲面,gl 扩展)
代码块里通过 info string 的属性声明扩展:```echarts {extensions="gl"}。
6. Plotly.js 测试
Plotly.js 的 partial bundle(basic / gl3d / cartesian 等)互相覆盖全局 Plotly,无法叠加。因此加载策略是:默认只加载最小包(plotly-basic,含 scatter/bar/pie);当页面里同时出现最小特性图表和声明了扩展的图表(如 3D 的 extensions="gl3d")时,自动升级到完整包。本页同时有 2D 和 3D,所以会用完整包渲染。
6.1 图片语法引用 .plotly.json(2D 数学函数)
6.2 代码块方式(2D 数学函数)
6.3 Shortcode 引用外部文件(3D 曲面,gl3d 扩展)
6.4 Shortcode 内联方式(3D 曲面,gl3d 扩展)
6.5 代码块方式(3D 曲面,gl3d 扩展)
7. D2 测试
D2 使用构建时渲染。2026-10-07 核对官方 @d2lang/d2@0.1.34 发布包时,浏览器入口 dist/browser/index.js 的未压缩大小是 11,514,165 字节(约 11.5 MB),本地以 gzip 默认级别压缩后为 8,559,566 字节(约 8.56 MB),具体数值随压缩实现略有差异;这是该入口文件的测量值,实际传输量还受 CDN 编码和缓存影响。D2.js 能在浏览器中通过 Web Worker 和 WASM 编译、渲染图表,适合需要在线编辑的页面。本站主要展示文章里的静态图,采用 CLI 输出 SVG,可以省去读者端的编译器下载和运行。D2.js 接入文档、发布包文件列表 和 SVG 导出文档 分别说明这几项依据。
四种入口都由 Hugo 模板收集内容,构建脚本调用 D2 CLI 后,Hugo 将 SVG 内联进页面。这样保留了 SVG 中的链接和 tooltip;如果使用普通 <img> 引用 SVG,链接不会生效,具体差异见 D2 导出 FAQ。同一文件的不同引用使用不同的 SVG ID,避免本页重复引用时相互干扰。窄屏下保留 SVG 的原始尺寸并允许横向滚动,避免把文字缩到难以阅读。
本地需要 Python 3.9+、Hugo 和 D2 CLI;本节使用 D2 v0.9.0。按 D2 安装文档 安装后,构建命令如下。--buildDrafts 等 Hugo 构建参数原样传给正式构建;CI 和 deploy.sh 也调用同一脚本。
python scripts/build-d2.py --buildDrafts --renderToMemory若工具不在 PATH 中,可设置 HUGO_BINARY 和 D2_BINARY 为可执行文件路径。脚本每次重新渲染引用的图表,包含相对导入的 .d2 文件和本地图片变更;生成文件位于已忽略的 assets/d2-generated/。修改 D2 内容或依赖后,应重新执行该脚本,再用 hugo server 预览,脚本自身不提供 watch 模式。直接执行 Hugo 时,缺少所需 SVG 会报错,不会退回显示源码或占位图。
src 只接受相对文章目录的本地路径;外部文件中的相对导入和图片以该文件所在目录为基准,内联图以文章目录为基准。布局、主题和留白等参数写在 D2 的 vars.d2-config 中。当前接入只导出根画板;多画板切换动画和 PNG 导出不在本节测试范围内;单画板的虚线流动动画见下方示例。图中使用远程 icon 时,构建仍需访问该地址;本节用例没有远程图像依赖。
7.1 图片语法引用 .d2 文件
此图展示 D2 文件、构建时渲染和 SVG 输出的关系。图片标题中的尺寸参数沿用其他图表的写法。
7.2 代码块方式
这个用例检查代码块属性、中文标签,以及 D2 字符串中的 < 和 & 是否正确保留。
7.3 Shortcode 引用外部文件
这里再次引用图片语法使用的文件,检查重复图表的 ID 是否独立。图中 SVG 节点链接到 D2 导出文档,D2 文件 节点包含 tooltip。
{{< d2 src="2602-test-diagram.d2" alt="D2 外部文件 Shortcode 测试" max-width="720px" >}}{{< /d2 >}}7.4 Shortcode 内联方式
内联用例使用容器和数据库形状,检查 Shortcode 内容与样式参数的组合。
7.5 虚线流动动画
7.1 和 7.3 复用的 .d2 文件已给连线添加以下属性。动画由 SVG 自带的 CSS 执行,不需要读者下载 D2 运行时;系统设置“减少动态效果”时,本站会停止这些动画。这里的运动用于示意数据流向,并不代表实际流量。
source -> build -> svg: {
style.animated: true
style.stroke-dash: 3
}animated 和 stroke-dash 是 D2 的原生样式,具体约束见 D2 连线样式。
PlantUML 测试
PlantUML 也在构建时生成内联 SVG,沿用 python scripts/build-d2.py 入口;脚本名称为兼容原有部署命令而保留。需要 Java、PlantUML JAR 和 Graphviz,设置 PLANTUML_JAR 为本地 JAR 路径;Java 不在 PATH 中时设置 JAVA_BINARY。本节采用 PlantUML 1.2026.8,CI 固定同一版本并校验 SHA-256。
每个入口放一组 @startuml / @enduml。外部文件只接受相对文章的本地路径;本地 !include 相对该文件解析。构建器使用 ALLOWLIST 安全配置,允许仓库内的文件和内置主题,不允许远程 !includeurl。源码错误会终止构建,不把错误图片当成正常 SVG 发布。
图片语法引用 .puml 文件
此图说明文章、构建脚本和 PlantUML 渲染器之间的数据传递。.plantuml 后缀也由同一入口识别。
代码块方式
这个组件图检查中文、Graphviz 布局以及代码块尺寸属性。
Shortcode 引用外部文件
重复引用同一张时序图,检查各次输出的 SVG ID 是否独立。
Shortcode 内联方式
这个状态图使用 !theme cerulean-outline 覆盖全站默认的 plain,检查文章级主题是否保留。
图表主题
页面外框使用深蓝灰背景,正文和图表仍在浅色阅读区内。选主题时比较了深色、灰度和浅色方案,最终采用浅色画布,流程图和数值图以蓝青色为主,PlantUML 使用深色线条;数值图保留多个可辨认的系列颜色,文章显式指定的颜色仍然优先。运行时默认值集中在主题的 partials/diagram-theme.html,没有额外加载主题包。
| 工具 | 采用的主题或默认值 | 取舍与覆盖方式 |
|---|---|---|
| Mermaid | base + 蓝青色 themeVariables | 相比打印取向的 neutral 增加浅色分组;可通过图内配置覆盖。主题文档 |
| D2 | 本节示例使用 Cool Classics,theme-id: 4 | 使用内置冷色主题,参数留在 .d2 源码中,其他文章不被强制改色。主题列表 |
| PlantUML | plain | 白底、深色文字与线条、无阴影;优先保证时序图标签的对比度。图内 !theme 可覆盖。内置主题 |
| ECharts | 本地蓝青色调色板、浅灰网格 | 使用原生主题对象,不下载额外主题脚本;option 中的显式样式优先。样式机制 |
| Plotly.js | 本地浅色 layout.template | 默认设置调色板、字体和坐标网格;图内 layout 或 template 可覆盖。模板机制 |
| Chart.js | 系统字体、深灰文字、浅灰网格 | 保留内置 Colors 插件和数据集的显式配色,避免改掉数据的颜色含义。颜色机制 |
| Excalidraw / Draw.io | 保留文件内的主题和颜色 | 草图、架构图的局部颜色常有含义,网站不对导出的画布做滤镜或强制反色。 |
8. 测试结果
| 类型 | 图片语法 | 代码块方式 | Shortcode (内联) | Shortcode (src) |
|---|---|---|---|---|
| Draw.io/diagrams.net | ✓ .drawio | ✓ drawio | ✓ | ✓ |
| Mermaid | ✓ .mermaid | ✓ mermaid | ✓ | ✓ |
| Excalidraw | ✓ .excalidraw | ✓ excalidraw | ✓ | ✓ |
| Chart.js | ✓ .chart.json | ✓ chart / chartjs | ✓ | ✓ |
| ECharts | ✓ .echarts.json | ✓ echarts | ✓ | ✓ |
| Plotly.js | ✓ .plotly.json | ✓ plotly | ✓ | ✓ |
| D2 | ✓ .d2 | ✓ d2 | ✓ | ✓ |
| PlantUML | ✓ .puml / .plantuml | ✓ plantuml | ✓ | ✓ |
D2 在 Windows 上使用 Hugo v0.167.0 和 D2 v0.9.0 验证:四种入口均生成 SVG;Edge 禁用 JavaScript 和外部请求后仍能显示图表,1280px 与 375px 视口下分别检查了完整显示和横向滚动。相对导入、本地图片、依赖变更重渲染、重复 ID,以及缺文件和语法错误阻止正式构建的检查也已通过。CI 已接入相同构建脚本和集成测试;远程 GitHub Actions 尚未运行。
本次新增检查使用 PlantUML 1.2026.8:四种入口、Graphviz 组件图、中文、本地 !include 重渲染、主题覆盖、重复 SVG ID、清理过期图表,以及缺文件、语法错误和远程 include 阻止发布的测试通过。D2 的虚线偏移随时间变化,并在“减少动态效果”模式下停止。
页面布局使用 Playwright 的 Chromium、Firefox 和 WebKit,在 320–2560px 的 13 组视口下检查首页、本文和 About 页,共 117 组页面/视口组合。模拟广告填充、未填充和列表整行插入均未造成页面横向溢出或卡片重叠;同时启用所有旧手动广告配置项时,首页最多两个、文章末尾一个,noad 页面不输出广告位。这里验证的是模拟注入,不代表真实 AdSense 投放或后台自动广告配置已经验证。
图表主题使用 Mermaid 12.1.0、Chart.js 4.5.1、ECharts 6.1.0 和 Plotly.js 4.1.2 的实际库在 Chromium 中验证,检查了 320px、390px、768px 和 1440px 宽度。测试库来自本地 npm 包,没有将 CDN 网络可用性或真实移动设备纳入结论。截图检查中修复了 Plotly 占位文字挤占绘图区的问题,并按 Plotly v3 迁移文档 将本节的图表及坐标轴标题改成 title.text。