跳转至

Web 数学渲染引擎漫谈:MathJax 与 KaTeX

一句话结论:MathJax 和 KaTeX 是 Web 上最流行的两种 LaTeX 公式渲染方案,前者以兼容性与完整性取胜(支持几乎所有 TeX 语法),后者以速度和轻量著称;两者各有千秋,但都与 pymdownx.arithmatex 这条 Python Markdown 生态链有微妙的耦合关系——选错组合会触发诡异的解析错误。

一、起源:两条不同的路

MathJax

MathJax 最初由 John Denker 于 2006 年发起,灵感来自一个简单的问题:为什么网页上的数学公式总是显示得那么糟糕?

早期的解决方案是把公式画成图片再嵌入 HTML——这能保证一致性的视觉效果,但带来了三个致命缺点:

  • 图片无法缩放;
  • 字号不随页面字体改变;
  • 可访问性差(屏幕阅读器读不到公式内容)。

Denker 的思路是让浏览器直接用 CSS + SVG / HTML/CSS 来渲染公式——这要求实现一个完整的 TeX 排版引擎,工作量巨大。2008 年的第一版发布后,MathJax 逐渐演变成一个开源社区项目,核心贡献者包括 David Loeffler、Steven Wood 等人。2019 年发布的 v3 是一次彻底重写:改用 CHTML(CSS + HTML)作为默认输出格式(原来同时支持 SVG、Common HTML 和 Native MML),并引入了全新的加载架构——这让 MathJax v3 的体积从 2MB+ 降到了约 800KB(gzip),成为目前最适合 Web 的版本。

KaTeX

KaTeX 的故事则更短、更聚焦。它由 Kevin Masson 和 Alex Wolfe 在 Khan Academy(可汗学院)内部开发,2015 年作为开源项目首次对外发布。设计哲学很直接:不要试图兼容所有 LaTeX,只做用户真正需要的子集,并且要快。

Khan Academy 的核心产品是一个在线练习平台,每道题的题干都包含大量 LaTeX 公式——如果页面加载因为等公式渲染而卡顿,用户体验会大打折扣。于是他们选择:

  • 用 同步渲染 代替异步等待;
  • 只实现高频使用的命令(\frac、\sqrt、\sum、矩阵环境等),砍掉冷门宏包;
  • 预编译 CSS 规则来加速常见表达式。

结果就是今天那个体积只有 MathJax 三分之一的轻量级引擎。KaTeX 至今仍在 Khan Academy 维护,最新版本是 v0.16.x(截至 2026 年)。

二、共性与差异

共同点

维度 说明
输入格式 两者都接受标准 LaTeX 数学符号 $\alpha$、$$\int_0^1 x\,dx$$ 作为输入
输出方式 最终都在 DOM 里生成 <span> / <div> 包裹的 HTML + CSS
CDN 分发 两者都有 jsDelivr / unpkg 等公共 CDN,可直接 <script src> 引用
配置机制 都通过全局变量(window.MathJax / window.__khtml 或全局 katex)控制行为
错误处理 解析失败时都会抛出错误并在页面上显示红色警告提示

核心差异

维度 MathJax v3 KaTeX v0.16
渲染时机 异步(typesetPromise() 返回 Promise) 同步(renderToString() 立即返回 HTML 字符串)
LaTeX 覆盖 几乎 100%(含 TikZ、amsthm、mathtools 等) 约 70%(不支持 TikZ,部分 amssymb 命令缺失)
首屏速度 较慢(需要下载 font cache + typesetting) 极快(通常 < 50ms)
SPA 兼容性 原生支持(异步重排) 需手动调用
.arithmatex 兼容 ✅ 内置(processArithmatex:true) ❌ 无原生支持,需自行遍历
体积(gzip) tex-chtml ≈ 800KB katex.min.js ≈ 550KB + katex.min.css ≈ 17KB
离线支持 支持打包本地资源 支持打包本地资源
适用场景 学术论文网站、全功能需求 教学平台、移动端优先、追求首屏性能

三、使用方式概览

MathJax 的基本用法

<!-- 1. 提前配置 -->
<script>
window.MathJax = {
  tex: { inlineMath: [['\\(', '\\)']] },
  options: { skipHtmlTags: ['script', 'noscript', 'style', 'textarea'] }
};
</script>
<!-- 2. 加载库(必须紧接配置之后) -->
<script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js" async></script>

正文中写 \(e^{i\pi}+1=0\),MathJax 会在页面加载完毕后自动扫描并重绘。

KaTeX 的基本用法

<!-- 1. 引入样式与库 -->
<link rel="stylesheet" href="katex.min.css">
<script src="katex.min.js"></script>
<script defer src="auto-render.min.js"></script>
<!-- 2. 正文 -->
<p>$E=mc^2$ 是著名公式。</p>
<!-- 3. 初始化(也可手动调用 renderMathInElement) -->
<script>
renderMathInElement(document.body, { delimiters: [
  { left: '$$', right: '$$', display: true },
  { left: '$',  right: '$',  display: false }
]});
</script>

注意 KaTeX 的 auto-render 脚本会扫描原始文本节点中的裸 $...$——一旦被其他插件(如 pymdownx)包装进 <span class="arithmatex"> 里,它就找不到了。

四、与本项目的关系

这个项目(MOOC · CS 自学课程指南)使用 Zensical 构建,Markdown 处理依赖 pymdownx.arithmatex。以下是关键决策链路:

发生了什么

graph LR
    A["$e$ in Markdown"] --> B[pymdownx.arithmatex generic=true]
    B --> C["<span class='arithmatex'>\\(e\\)</span>"]
    C --> D{Which renderer?}
    D -->|"MathJax"| E["✅ processArithmatex: true"]
    D -->|"KaTeX"| F["❌ 读到 \\(e\\),报错"]

pymdownx.arithmatex.generic = true 会把全文的所有 \(...\) 包裹替换成带 .arithmatex 类的 span/div,原始裸公式在 DOM 中已经不存在了。这意味着:

  • KaTeX 的 auto-render 找不到任何 \(...\) → 完全失效;
  • 即使我们自己遍历 .arithmatex 节点,拿到的文本是 \e\(转义后的原文),KaTeX 会尝试把 \e 当作 TeX 命令执行,导致 ParseError: Can't use function '\(' in math mode;
  • MathJax v3 的 processArithmatex: true 是唯一原生认识这种结构的渲染器。

本项目的具体配置

zensical.toml:

[[project.extra.polyfills]]
path = "https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"
async = true

extra_javascript = [
  "javascripts/mathjax.js",   # ← 放置 window.MathJax 配置
  ...
]

docs/javascripts/mathjax.js:

// 必须在加载 tex-chtml.js 之前设置
window.MathJax = {
  tex: { processArithmatex: true },  // 关键开关
  // ...
};

document$.subscribe(function () {
  // Zensical 即时导航触发重排
  MathJax.startup.promise.then(MathJax.typesetPromise);
});

为什么不用 KaTeX

在本项目的技术栈下(Zensical + pymdownx.arithmatex),切换成 KaTeX 有两种路径:

  1. 禁用 arithmatex:但这会让所有现有文档失去公式支持(pymdownx.arithmatex 是默认开启的,且本项目很多已有文件依赖它);
  2. 手动遍历 span 并剥掉 \( \) 外壳:可行但不优雅,还需要额外处理 \[ \] 显示模式判断逻辑,属于重复造轮子。

相比之下,MathJax 只需要一条配置就能无缝工作,而且对复杂公式(\mathbb{R}、\begin{pmatrix}、TikZ 等)的支持也更完整——适合知识型网站的定位。

五、选型建议

根据你的场景对号入座:

场景 推荐方案
学术/技术博客,公式种类多 MathJax v3
教学平台,首屏性能敏感 KaTeX
使用 Sphinx / MkDocs + pymdownx MathJax(via processArithmatex)
纯静态 HTML,无预处理层 KaTeX auto-render 或 MathJax 均可
需要 KaTeX 风格但想保留 .arithmatex 手写预处理:把 \(...\) → \( ... \) 去掉外壳后再渲染

六、进一步阅读