境内博客想用 Mermaid 画流程图,官方文档会让你引 jsdelivr CDN——实测不可靠。这篇是这个博客真实的落地路径:自托管 + 按需加载 + gzip,每一步都有验证命令和实测数字。
1. 拿到 mermaid.min.js(不走 CDN)
jsdelivr/GitHub 直连都不稳,npmmirror 的文件直链稳定可用:
# 先查最新版本
curl -s https://registry.npmmirror.com/mermaid/latest | \
python3 -c "import sys,json;print(json.load(sys.stdin)['version'])"
# → 12.2.0
# 直链下载(注意 /files/ 路径形式)
mkdir -p static/js
curl -o static/js/mermaid.min.js \
"https://registry.npmmirror.com/mermaid/12.2.0/files/dist/mermaid.min.js"
# 验证:文件尾部应有全局暴露语句
tail -c 200 static/js/mermaid.min.js
# → globalThis["mermaid"] = ... ✓ 确认 UMD 全局可用
5,494,801 字节。直接上线太重?看第 4 步。
2. 渲染钩子:```mermaid 代码块变成图
站点级新建 layouts/_markup/render-codeblock-mermaid.html(Hugo render hook,两行):
<pre class="mermaid">
{{ .Inner | htmlEscape | safeHTML }}
</pre>
{{ .Page.Store.Set "hasMermaid" true }}
文章里写普通代码块、语言标 mermaid 即可。关键在 Store.Set——它给模板间传了个标记。
3. 按需加载:只有含图的页面才背 5.5MB
覆写 layouts/_partials/extend_head.html(主题的 head 钩子):
{{- if .Store.Get "hasMermaid" }}
<script src="/js/mermaid.min.js" defer></script>
<script>
window.addEventListener('DOMContentLoaded', function () {
mermaid.initialize({
startOnLoad: true,
theme: 'dark',
themeVariables: {
background: '#16161e',
primaryColor: '#1f2430',
primaryTextColor: '#e8eaed',
primaryBorderColor: '#6366f1',
lineColor: '#8b8fa3',
clusterBkg: '#12141c'
},
flowchart: { curve: 'basis', padding: 10 }
});
});
</script>
{{- end }}
两个决策说明:固定 dark 主题而不是跟随站点主题——图表容器与代码块统一深色,避免了浅色主题下图表配色割裂,也省掉主题切换时的重绘逻辑;themeVariables 逐项定制让节点底色、连线色与站点设计系统一致。
验证按需生效:
grep -l "mermaid.min.js" public/posts/tech -r | wc -l # 只有含图页面
grep -c "mermaid.min.js" public/posts/tech/prompt-engineering/patterns/core-patterns/index.html
# → 0 无图页面不加载 ✓
4. gzip:5.5MB → 1.5MB
nginx server block 里加(CDN 没有的,自己给):
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_comp_level 6;
gzip_types application/javascript text/css image/svg+xml application/json;
注意 gzip_types 必须显式列出 js/svg——nginx 默认只压 text/html,不配就是裸传。实测:
curl -s -o /dev/null -w "%{size_download}B\n" \
-H "Accept-Encoding: gzip" https://blog.jocab.xyz/js/mermaid.min.js
# → 1572825B ≈ 1.5MB(原 5.5MB,压缩率 71%)
配合浏览器缓存(location /js/ { expires 30d; }),用户只有第一次访问含图页面时付这 1.5MB。
5. 全链路验收清单
# ① 文章里 mermaid 块正确渲染
curl -s https://site/some-post/ | grep -c 'class="mermaid"'
# ② 含图页面加载脚本、无图页面不加载(见第 3 步)
# ③ 脚本线上可取且已压缩(见第 4 步)
# ④ 视觉:无头 Chrome 截图确认图形真的画出来了
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \
--headless=new --virtual-time-budget=12000 \
--screenshot=/tmp/mermaid-check.png https://site/some-post/
第 ④ 步别省:virtual-time-budget 给 JS 留渲染时间,截图能抓到"脚本加载了但图画失败"这类静默错误。
成本总结:一个 5.5MB 的静态文件、一段 15 行的钩子、六行 nginx 配置——换来全站任意文章零门槛画流程图/时序图,且无图页面零开销、境内访问零依赖。