1. 问题现象
Mooglade 从 v16.2.0 开始支持 Mermaid 渲染,在文章中使用 Markdown Mermaid 代码块时,本地开发环境可以渲染图表,但线上仅显示 Mermaid 源代码,不生成 SVG 图表。
受影响的 Markdown 格式示例:
```mermaid
flowchart LR
A --> B
```
2. 初始排查
检查线上文章生成的 HTML。
确认 Markdown 已正确转换为:
<pre><code class="language-mermaid">...</code></pre>确认文章页面已加载带指纹的资源:
/js/3rd/mermaid.min.zfjo3jmytt.js /js/app/post.2xfib7zcr8.mjs检查浏览器运行状态:
typeof globalThis.mermaid
线上结果为 undefined,而本地预览页结果为 object。
检查渲染结果:
document.querySelectorAll('.post-content pre.mermaid svg').length
线上为 0,本地为 1。
3. 前端渲染机制
文章页加载 post.mjs,其调用 post.mermaid.mjs:
- 查找
.post-content pre code.language-mermaid或.lang-mermaid。 - 从
globalThis.mermaid获取 Mermaid API。 - 将代码块转换为
pre.mermaid。 - 调用
mermaid.run({ nodes })生成 SVG。
由于线上 globalThis.mermaid 不存在,渲染函数会直接返回,不会抛出明显错误。
4. 根因
本地与线上原始 Mermaid JavaScript 文件的 SHA-256 相同,不是版本或源码差异。
差异在 HTTP 内容协商:
- 本地 Kestrel 返回有效的
gzip资源。 - 线上浏览器请求头包含
Accept-Encoding: br, gzip,优先获得 Brotli (br) 资源。 - 发布目录中的
/path/to/app/wwwroot/js/3rd/mermaid.min.js.br初始大小为0字节。 - 发布清单
/path/to/app/Mooglade.staticwebassets.endpoints.json中,Mermaid Brotli 条目的Content-Length也为0,ETag 是空内容哈希。
因此浏览器表面上成功加载了 Mermaid 脚本 URL,实际执行的是空响应,导致 globalThis.mermaid 未定义。
5. 修复动作
安装 Brotli 命令行工具:
apt-get update apt-get install -y brotli从原始脚本重新生成 Brotli 副本:
brotli -f -q 11 /path/to/app/wwwroot/js/3rd/mermaid.min.js修复静态资源清单中所有指向
js/3rd/mermaid.min.js.br的条目。需要同步更新以下字段:
Content-Length:实际.br文件长度ETag:.br文件的 SHA-256 Base64 值,带双引号EndpointProperties中的integrity:sha256-加相同的 SHA-256 Base64 值
本次共修复 4 个路由:
js/3rd/mermaid.min.js js/3rd/mermaid.min.js.br js/3rd/mermaid.min.zfjo3jmytt.js js/3rd/mermaid.min.zfjo3jmytt.js.br修改清单前已创建备份:
/path/to/app/Moonglade.Web.staticwebassets.endpoints.json.bak-YYYYMMDDHHMMSS重启应用加载新的静态资源清单:
6. 修复验证
验证 Brotli 响应:
curl --compressed -sS -D - -o /dev/null \
-H 'Accept-Encoding: br, gzip' \
https://example.com/js/3rd/mermaid.min.<hash>.js
预期响应:
Content-Encoding: br
Content-Length: 739278
浏览器端验证:
({
mermaidPresent: typeof globalThis.mermaid !== 'undefined',
renderedMermaidBlocks: document.querySelectorAll('.post-content pre.mermaid svg').length
})
本次修复后线上结果:
{
mermaidPresent: true,
renderedMermaidBlocks: 1
}
7. 用户侧缓存处理
Mermaid 带指纹资源使用:
Cache-Control: max-age=31536000, immutable
故障期间已缓存空 Brotli 响应的浏览器可能不会在普通刷新时重新请求资源。用户需使用强制刷新:
- Windows/Linux:
Ctrl + Shift + R - macOS:
Command + Shift + R
8. 后续部署注意事项
下次发布时,必须将原始静态文件、
.gz/.br预压缩文件和应用的staticwebassets.endpoints.json清单作为同一批发布产物整体传输。不要只覆盖 DLL 或原始 JavaScript 文件,否则压缩文件与静态资源清单可能失配。
发布完成后抽查关键资源的压缩响应,尤其是浏览器会优先请求 Brotli 的环境:
curl --compressed -sS -D - -o /dev/null \ -H 'Accept-Encoding: br, gzip' \ https://example.com/js/3rd/mermaid.min.<hash>.js若再次出现同类问题,应优先检查:
stat -c '%s %n' /path/to/app/wwwroot/js/3rd/mermaid.min.js*以及静态资源清单中的
Content-Length、ETag和integrity是否与实际压缩文件一致。本次服务器端清单修复是发布产物损坏后的补救措施。长期解决方案是修复或校验部署流程,确保发布前生成并验证所有预压缩静态资源。
Comments