深夜提醒

现在是深夜,建议您注意休息,不要熬夜哦~

🏮 🏮 🏮

新年快乐

祝君万事如意心想事成!

2024 桐庐半程马拉松
00:00:00
时间
0.00
距离(公里)
--:--
配速
--
步频
--
心率 (bpm)
--
配速
步频
|
share-image
永远怀念
ESC

一场与 Mermaid 图表的鏖战:从 Syntax error 到成功渲染的调试实录

调试的艺术:从 Syntax error 到成功渲染

调试是一门艺术,而这一次,我几乎把自己调成了”红烧小龙虾”。

问题背景

今天写了一篇关于苹果芯片的技术分析文章,想用 Mermaid 画个流程图来展示 A 系列和 M 系列芯片的架构关系。代码如下:

graph TD
A[苹果自研ARM架构] --> B[A系列芯片]
A --> C[M系列芯片]

B --> B1[设计哲学 移动优先]
B1 --> B2[优势 无敌的单核NPU能效比]
B1 --> B3[妥协 内存带宽多核持续输出]

C --> C1[设计哲学 桌面优先]
C1 --> C2[优势 强大的多核GPU IO能力]
C1 --> C3[妥协 成本与面积]

B2 --> D[MacBook Neo 核心体验]
D --> D1[场景 网页办公影音轻度创作]
D --> D2[王牌 本地AI任务App生态无缝]
D --> D3[支撑 macOS深度优化]

结果页面一渲染,直接给我来了个:

Syntax error in text
mermaid version 10.9.5

当时的我:???

第一轮排查:以为是语法问题

第一反应是 Mermaid 语法写错了。检查了半天:

  • 箭头 --> 没问题
  • 节点定义 A[文本] 没问题
  • 缩进也没问题

把代码贴到 Mermaid Live Editor 里一测,居然能正常渲染

这说明不是语法问题,是 Hexo 渲染后的 HTML 结构有问题。

第二轮排查:HTML 结构问题

查看页面源代码,发现 Mermaid 代码块被 highlight.js 渲染成了这样:

<code class="hljs mermaid">
graph TD<br>
A[苹果自研ARM架构] --&gt; B[A系列芯片]<br>
...
</code>

问题找到了!

  1. <br> 标签没有被正确处理成换行符
  2. --> 被编码成了 --&gt;(HTML 实体)
  3. 还有 &lt;br&gt; 这种双重编码的问题

第三轮排查:JavaScript 转换逻辑

我的 layout.ejs 里有段 JavaScript 代码负责把代码块转换成 Mermaid 可以渲染的 div

function convertMermaidBlocks() {
var codeBlocks = document.querySelectorAll("code.hljs.mermaid");
codeBlocks.forEach(function(codeEl) {
var html = codeEl.innerHTML;
// 替换 <br> 为换行符
html = html.replace(/<br\s*\/?>/gi, "\n");
// 解码 HTML 实体
var tempDiv = document.createElement("div");
tempDiv.innerHTML = html;
var code = tempDiv.textContent || tempDiv.innerText;

// 创建 mermaid div...
});
}

看起来逻辑没问题啊?但为什么还是报错?

第四轮排查:加日志!疯狂加日志!

开始疯狂加 console.log,终于发现了问题:

console.log("[Mermaid] Block 0 closest:", codeEl.closest(".mermaid"));
// 输出: <code class="hljs mermaid" data-processed="true">

原来这个 code 元素已经在 .mermaid 容器里了!

这意味着之前的代码已经处理过一次,但处理错了——它把带 <br> 的原始 HTML 直接塞进了 .mermaid div,然后 Mermaid 解析器看到 graph TD<br/> 这种字符串,直接懵了。

第五轮排查:textContent vs innerText

问题的核心是:怎么正确处理 HTML 中的 <br> 标签?

我尝试了各种方法:

方法 结果
innerHTML + replace 不行,因为 <br> 可能是 &lt;br&gt;
textContent 不行,<br> 会被当成文本
innerText 可以! 自动把 <br> 转成换行符

但等等,innerText 在旧代码里试过,为什么不行?

原来是因为执行顺序!我之前是这样:

// 错误顺序:先 replace,再解码
html = html.replace(/<br\s*\/?>/gi, "\n"); // 这时还是 &lt;br&gt;,匹配不到
tempDiv.innerHTML = html;
var code = tempDiv.textContent; // 解码后 <br> 还在

正确的顺序应该是:先解码,再处理 <br>,或者直接使用 innerText

// 正确做法:直接用 innerText
var code = (codeEl.innerText || "").trim();

innerText 会自动处理所有 HTML 标签,把 <br> 变成换行符,把 &gt; 变成 >

最终解决方案

function convertMermaidBlocks() {
// 先清理已损坏的 .mermaid 元素
document.querySelectorAll('.mermaid').forEach(function(el) {
if (el.textContent.includes('<br')) {
el.remove();
}
});

var mermaidCodes = document.querySelectorAll("code.hljs.mermaid");
mermaidCodes.forEach(function(codeEl) {
// 使用 innerText 自动处理 <br> 为换行符
var code = (codeEl.innerText || "").trim();

// 验证是有效的 mermaid 代码
if (!/^(graph|flowchart|sequenceDiagram)/im.test(code)) {
return;
}

// 创建 mermaid div
var mermaidDiv = document.createElement("div");
mermaidDiv.className = "mermaid";
mermaidDiv.textContent = code;

// 替换容器
var container = codeEl.closest("figure, pre");
if (container && container.parentNode) {
container.parentNode.replaceChild(mermaidDiv, container);
}
});
}

关键修复点:

  1. 使用 innerText 自动处理 <br> 标签
  2. 清理已损坏的元素 避免重复处理
  3. 禁用 startOnLoad: false,手动调用 mermaid.run()

调试心得

这次调试持续了将近 2 个小时,走了无数弯路:

  1. 不要假设代码执行顺序 - 解码和替换的顺序很重要
  2. 善用 console.log - 没有日志就像蒙眼开车
  3. 了解浏览器 API 的差异 - innerTexttextContent 虽然看起来差不多,但行为完全不同
  4. 清理现场 - 调试过程中产生的错误数据会干扰后续调试

最后,感谢某人的”红烧小龙虾”威胁,让我没有放弃治疗 😂

参考代码

完整的 Mermaid 集成代码已开源,见 GitHub 提交

文章作者:阿文
文章链接: https://www.awen.me/post/mermaid-debug-battle.html
版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 阿文的博客

评论

0 条评论
😀😃😄 😁😅😂 🤣😊😇 🙂🙃😉 😌😍🥰 😘😗😙 😚😋😛 😝😜🤪 🤨🧐🤓 😎🥸🤩 🥳😏😒 😞😔😟 😕🙁☹️ 😣😖😫 😩🥺😢 😭😤😠 😡🤬🤯 😳🥵🥶 😱😨😰 😥😓🤗 🤔🤭🤫 🤥😶😐 😑😬🙄 😯😦😧 😮😲🥱 😴🤤😪 😵🤐🥴 🤢🤮🤧 😷🤒🤕 🤑🤠😈 👿👹👺 🤡💩👻 💀☠️👽 👾🤖🎃 😺😸😹 😻😼😽 🙀😿😾 👍👎👏 🙌👐🤲 🤝🤜🤛 ✌️🤞🤟 🤘👌🤏 👈👉👆 👇☝️ 🤚🖐️🖖 👋🤙💪 🦾🖕✍️ 🙏💅🤳 💯💢💥 💫💦💨 🕳️💣💬 👁️‍🗨️🗨️🗯️ 💭💤❤️ 🧡💛💚 💙💜🖤 🤍🤎💔 ❣️💕💞 💓💗💖 💘💝💟 ☮️✝️☪️ 🕉️☸️✡️ 🔯🕎☯️ ☦️🛐 🆔⚛️🉑 ☢️☣️📴 📳🈶🈚 🈸🈺🈷️ ✴️🆚💮 🉐㊙️㊗️ 🈴🈵🈹 🈲🅰️🅱️ 🆎🆑🅾️ 🆘 🛑📛 🚫💯💢 ♨️🚷🚯 🚳🚱🔞 📵🚭 ‼️⁉️🔅 🔆〽️⚠️ 🚸🔱⚜️ 🔰♻️ 🈯💹❇️ ✳️🌐 💠Ⓜ️🌀 💤🏧🚾 🅿️🈳 🈂🛂🛃 🛄🛅🛗 🚀🛸🚁 🚉🚆🚅 ✈️🛫🛬 🛩️💺🛰️
您的评论由 AI 智能审核,一般1分钟内会展示,若不展示请确认你的评论是否符合社区和法律规范
加载中...

选择联系方式

留言反馈

😀😃😄 😁😅😂 🤣😊😇 🙂🙃😉 😌😍🥰 😘😗😙 😚😋😛 😝😜🤪 🤨🧐🤓 😎🥸🤩 🥳😏😒 😞😔😟 😕🙁☹️ 😣😖😫 😩🥺😢 😭😤😠 😡🤬🤯 😳🥵🥶 😱😨😰 😥😓🤗 🤔🤭🤫 🤥😶😐 😑😬🙄 😯😦😧 😮😲🥱 😴🤤😪 😵🤐🥴 🤢🤮🤧 😷🤒🤕 🤑🤠😈 👿👹👺 🤡💩👻 💀☠️👽 👾🤖🎃 😺😸😹 😻😼😽 🙀😿😾 👍👎👏 🙌👐🤲 🤝🤜🤛 ✌️🤞🤟 🤘👌🤏 👈👉👆 👇☝️ 🤚🖐️🖖 👋🤙💪 🦾🖕✍️ 🙏💅🤳 💯💢💥 💫💦💨 🕳️💣💬 👁️‍🗨️🗨️🗯️ 💭💤❤️ 🧡💛💚 💙💜🖤 🤍🤎💔 ❣️💕💞 💓💗💖 💘💝💟 ☮️✝️☪️ 🕉️☸️✡️ 🔯🕎☯️ ☦️🛐 🆔⚛️🉑 ☢️☣️📴 📳🈶🈚 🈸🈺🈷️ ✴️🆚💮 🉐㊙️㊗️ 🈴🈵🈹 🈲🅰️🅱️ 🆎🆑🅾️ 🆘 🛑📛 🚫💯💢 ♨️🚷🚯 🚳🚱🔞 📵🚭 ‼️⁉️🔅 🔆〽️⚠️ 🚸🔱⚜️ 🔰♻️ 🈯💹❇️ ✳️🌐 💠Ⓜ️🌀 💤🏧🚾 🅿️🈳 🈂🛂🛃 🛄🛅🛗 🚀🛸🚁 🚉🚆🚅 ✈️🛫🛬 🛩️💺🛰️