Typecho + JOE 主题下 Mermaid 渲染的解决方案

Typecho + JOE 主题下 Mermaid 渲染的解决方案

AndyYan
2026-04-05 / 0 评论 / 21 阅读 / 正在检测是否收录...

阅前须知:由于博主现在 并没有太多自己写代码甚至脚本的能力 ,本篇文章在我和AI共同解决了这个问题之后 直接由AI生成 ,若你对此感到反感,可以现在 关闭该帖我是废物

一、问题背景

在 Typecho 中使用 Mermaid 一直不算难,但一旦换成 JOE 主题,问题就开始变得复杂:

  • 写好的 `mermaid 代码块被当成普通代码高亮
  • 插件明明启用了,但图表不渲染
  • 翻页(PJAX)后 Mermaid 直接失效

我一开始也尝试用常规方案(正则替换 HTML),结果发现:

👉 根本不稳定


二、问题的本质

JOE 主题做了很多“增强”,包括:

  • 自定义代码高亮(Prism / Highlight.js)
  • 改写 Markdown 输出结构
  • 使用 PJAX(局部刷新页面)

这导致一个核心问题:

你在后端生成的 HTML,很可能在前端被再次修改甚至覆盖

例如你期望的是:

<pre><code class="language-mermaid"></code></pre>

但实际可能变成:

<pre class="language-mermaid"></pre>

甚至:

<div class="joe_code">
  <pre>...</pre>
</div>

👉 结构不稳定 → 正则必炸


三、传统方案为什么不行?

常见插件思路:

Markdown → HTML → 正则替换 → <pre class="mermaid">

问题在于:

  • 依赖 HTML 结构(不可靠)
  • 容易被主题覆盖
  • PJAX 后不会重新执行

结论:

后端改 HTML,在 JOE 这种强主题下是错误方向

四、最终解决方案:前端接管

我最后采用的是:

完全绕过后端,前端动态解析 Mermaid

核心流程:

页面加载
↓
扫描所有 language-mermaid 代码块
↓
替换为 .mermaid DOM
↓
调用 Mermaid 渲染

五、核心实现解析

1. 扫描代码块

const blocks = document.querySelectorAll(
    'pre code.language-mermaid, pre.language-mermaid'
);

为什么这样写?

👉 兼容两种结构:

<pre><code class="language-mermaid"></code></pre>
<pre class="language-mermaid"></pre>

2. 提取原始代码

let code = codeBlock.textContent;

👉 直接拿文本,不依赖 HTML 结构


3. 重建 DOM

const container = document.createElement('div');
container.className = 'mermaid-container';

const mermaidDiv = document.createElement('div');
mermaidDiv.className = 'mermaid';
mermaidDiv.textContent = code;

container.appendChild(mermaidDiv);

最终结构:

<div class="mermaid-container">
  <div class="mermaid">...</div>
</div>

4. 替换原代码块

pre.replaceWith(container);

👉 关键点:

  • 删除原代码高亮 DOM
  • 避免主题再次干扰

5. 渲染 Mermaid

mermaid.initialize({
    startOnLoad: false,
    theme: getTheme()
});

mermaid.init(undefined, document.querySelectorAll('.mermaid'));

为什么不用自动加载?

👉 因为 DOM 是动态生成的


6. 防止重复渲染

if (codeBlock.dataset.mermaidDone) return;

👉 防止:

  • PJAX 重复执行
  • 多次渲染报错

7. 适配 PJAX(关键)

document.addEventListener('pjax:complete', run);

👉 没有这行:

❌ 翻页后 Mermaid 全部失效


六、主题适配(暗黑模式)

function getTheme() {
    if (document.body.classList.contains('dark')) {
        return 'dark';
    }
    return 'default';
}

👉 自动跟随主题切换


七、为什么这个方案最稳?

对比一下:

方案稳定性原因
后端正则替换依赖 HTML
修改 Markdown 解析被主题覆盖
前端接管(本方案)直接操作 DOM

八、核心设计思想

这次优化本质上是一次“架构调整”:

1️⃣ 不和主题抢控制权

JOE 已经接管了渲染链:

👉 你再插手,只会冲突


2️⃣ 前端才是最终执行层

只要页面上存在:

language-mermaid

👉 就一定能识别


3️⃣ 幂等设计

data-mermaidDone

👉 保证多次执行不会出问题


九、最终效果

  • ✅ 支持所有 Mermaid 图
  • ✅ 支持 PJAX
  • ✅ 不受代码高亮影响
  • ✅ 自动暗黑模式
  • ✅ 主题无关(通用)

十、一句话总结

与其试图修补被主题打乱的 HTML,不如直接绕过它,在前端重建渲染链。

十一、源码附上

十二、使用方法:

把这个文件放到:

/usr/plugins/Mermaid/Plugin.php

进入 Typecho 后台: 控制台 → 插件 → 启用 Mermaid
写文章时使用 Mermaid就直接在 Markdown 里写:

发布后就会自动渲染成图。

0

评论 (0)

取消