1. 为什么要在 MediaWiki 里折腾 Math 公式
如果你维护过面向理工科团队或教学场景的 Wiki,大概率会遇到这个需求:有人想写一段带积分、矩阵、希腊字母的推导过程,结果发现默认编辑器里只能敲纯文本,公式要么用图片贴上去,要么用一堆 HTML 实体硬拼,改一次就想砸键盘。我最早接手一个内部知识库时,物理组的同事直接在讨论页里抱怨“写个麦克斯韦方程组比推导它还累”,这就是典型的公式支持缺失。
MediaWiki 本身对数学公式是有原生方案的,早期靠 Math 扩展调用外部排版引擎渲染成 PNG,但部署链路长、依赖多,很多轻量环境根本跑不起来。后来社区里出现了基于前端渲染的方案,SimpleMathJax 就是其中比较省心的一种:它把渲染工作交给浏览器端的 MathJax 库,服务端只负责把公式原文包一层容器,不需要额外装排版服务,也不需要编译二进制。换句话说,你只要把扩展目录放进去、改一行配置,公式就能在页面上正常显示。
这个方案适合谁?我总结下来是三类人:一是自建 Wiki 的个人或小团队,服务器资源有限,不想为了公式单独维护一套渲染服务;二是内容以教学、笔记、论文草稿为主,公式更新频繁,需要所见即所得的编辑体验;三是运维基础一般,希望配置步骤越少越好。它的核心价值就一句话:用最小的部署成本,让 MediaWiki 具备可编辑、可搜索、可复制的数学公式能力。下面我把自己从零搭起来、踩坑、调优的完整过程拆开讲,尽量让第一次接触的人也能照着做下来。
2. 方案选型:为什么是 SimpleMathJax 而不是别的
2.1 原生 Math 扩展的痛点在哪
MediaWiki 官方那个 Math 扩展,功能确实全,支持 LaTeX 语法、能输出 MathML、还能做无障碍朗读。但它的部署门槛劝退了不少人。它依赖一个叫 Mathoid 的服务,或者更老的 texvc 二进制,前者要跑 Node.js 服务,后者要装 LaTeX 发行版。我在一台 2G 内存的小机器上试过,光装 TeX Live 就吃掉好几个 G 的磁盘,编译一次公式还要等半天,页面加载肉眼可见地卡。
更麻烦的是版本兼容。MediaWiki 每次大版本升级,Math 扩展的接口经常跟着变,配置项一改,之前调好的参数就失效。对于只想安安静静写公式的人来说,这种维护成本完全不划算。所以当需求只是“把 LaTeX 渲染出来”时,我倾向于绕开重型方案。
2.2 SimpleMathJax 的工作机制
SimpleMathJax 的思路很直接:它不碰服务端渲染,而是通过一个 MediaWiki 的标签扩展(tag extension)把<math>标签里的内容原样输出到一个带特定 class 的容器里,然后在前端加载 MathJax 的 JavaScript 库,由浏览器完成排版。整个链路里,服务端只做字符串处理,真正的计算发生在用户浏览器里。
这样做的好处很明显:部署简单,不依赖外部服务;公式是文本形式存在的,搜索引擎和站内搜索都能抓到;用户复制公式时拿到的是 LaTeX 源码,粘到别处还能用。代价也有,就是首次加载页面时要下载 MathJax 的脚本,大概几百 KB,不过现在浏览器缓存机制成熟,第二次访问基本无感。另外它对老旧的 IE 支持不好,但 2024 年了,这个问题可以忽略。
2.3 和其他轻量方案的对比
市面上还有几种做法,我简单列个表对比一下,方便你判断。
| 方案 | 服务端依赖 | 渲染位置 | 部署难度 | 适合场景 |
|---|---|---|---|---|
| 原生 Math 扩展 | Mathoid 或 texvc | 服务端 | 高 | 大型站点、需要 MathML |
| SimpleMathJax | 无 | 浏览器 | 低 | 中小站点、教学笔记 |
| 图片贴公式 | 无 | 无 | 极低 | 公式极少、不介意不可编辑 |
| 客户端脚本自写 | 无 | 浏览器 | 中 | 有定制需求的技术团队 |
从表里能看出来,SimpleMathJax 卡在“够用”和“省事”的平衡点上。图片方案虽然最简单,但公式不能改、不能搜、放大还糊,长期维护是灾难。自写脚本灵活,但你要自己处理标签解析、缓存、版本升级,投入产出比不高。所以对绝大多数中小规模 Wiki,我推荐先上 SimpleMathJax,等真有高性能或无障碍需求了再考虑迁移。
3. 部署前的环境确认与准备工作
3.1 确认 MediaWiki 版本和目录结构
动手之前先做两件事。第一,确认你的 MediaWiki 版本,进到 Wiki 根目录执行维护脚本,或者直接看includes/Defines.php里的版本常量。SimpleMathJax 对版本有一定要求,太老的版本(比如 1.25 以前)可能不兼容新版扩展的注册方式。我一般建议在 1.35 及以上的 LTS 版本上操作,稳定性和扩展生态都更好。
第二,搞清楚目录结构。标准安装下,扩展放在extensions/目录,配置文件是根目录的LocalSettings.php。你要有这两个路径的写权限。如果是虚拟主机或者容器环境,确认一下 PHP 的执行用户对扩展目录有读取权限,否则后面加载会报 500。
提示:改配置前先备份
LocalSettings.php,这个文件一旦写错,整个站点直接白屏,恢复起来很麻烦。
3.2 获取扩展文件的几种途径
获取 SimpleMathJax 一般有三种方式。最省事的是用 Git 克隆到扩展目录,命令大概是这样:
cd /path/to/mediawiki/extensions git clone https://github.com/example/SimpleMathJax.git如果你不方便用 Git,也可以下载打包好的压缩包,解压后把目录重命名为SimpleMathJax,注意大小写要和后面配置里引用的名字一致。还有一种是通过 Composer 管理,但对 MediaWiki 扩展来说,手动放置更直观,出问题也好排查。
我个人的习惯是克隆下来之后,先看一眼目录里的extension.json或者老的SimpleMathJax.php,确认入口文件存在。有些第三方打包的版本目录层级会多套一层,导致 MediaWiki 找不到注册文件,这是新手最容易踩的坑。
3.3 权限与安全的基础检查
扩展目录放好之后,检查一下文件属主。Web 服务器用户(常见的是www-data或apache)需要对扩展目录有读权限,但不需要写权限。如果你图省事给了 777,短期能跑,长期是安全隐患。正确做法是把属主设成 Web 用户,权限设成 755 目录、644 文件。
chown -R www-data:www-data /path/to/mediawiki/extensions/SimpleMathJax chmod -R 755 /path/to/mediawiki/extensions/SimpleMathJax另外确认一下 PHP 的allow_url_fopen之类的配置,虽然 SimpleMathJax 本身不依赖远程请求,但如果你的 MathJax 脚本打算走 CDN,就要保证前端能正常访问外部资源。内网环境的话,建议把 MathJax 库下载到本地,改成本地引用,避免外网不通导致公式一直转圈。
4. 核心配置与参数详解
4.1 在 LocalSettings.php 中启用扩展
配置这一步是整个流程的核心。打开LocalSettings.php,在文件末尾(或者你习惯的扩展加载区)加上这一行:
wfLoadExtension( 'SimpleMathJax' );这行代码的作用是告诉 MediaWiki 去extensions/SimpleMathJax目录读取注册信息,把扩展挂载进来。注意wfLoadExtension是较新版本的标准写法,如果你用的是很老的 MediaWiki,可能需要用require_once直接引入入口文件。加完之后保存,刷新一个页面,如果出现白屏或者报错,多半是路径或权限问题,回去检查目录名拼写。
4.2 关键配置项逐个拆解
SimpleMathJax 提供了一些可调参数,写在wfLoadExtension之后。我把常用的几个列出来,并说明每个参数背后的考量。
$wgSimpleMathJaxConfig = [ 'useLocalMathJax' => false, 'MathJaxUrl' => 'https://cdn.example.com/mathjax/tex-mml-chtml.js', 'config' => 'TeX-AMS_HTML', ];useLocalMathJax决定 MathJax 库是从本地加载还是从外部地址加载。内网或者对加载速度敏感的场景,建议设为true,然后把库文件放到扩展目录下的modules/里。MathJaxUrl是脚本地址,走外部的话要保证这个地址稳定可达。config指定 MathJax 的配置预设,TeX-AMS_HTML是最通用的,支持大部分 LaTeX 数学语法。
这里有个细节值得说:MathJax 3 和 MathJax 2 的配置方式差别很大。SimpleMathJax 的不同版本可能默认引用不同大版本的库,如果你发现公式渲染出来样式怪怪的,或者某些命令不识别,先确认库版本和配置是否匹配。我遇到过用 MathJax 3 的库却套了 2 的配置,结果矩阵环境直接报错,换成对应的配置就好了。
4.3 缓存与性能相关设置
MediaWiki 有页面缓存和解析器缓存,扩展改动后有时候不生效,是因为缓存没刷新。调试阶段可以在LocalSettings.php里临时关掉缓存:
$wgEnableParserCache = false; $wgCachePages = false;确认公式正常显示后,再把这两行去掉或者设回 true,否则站点性能会受影响。另外 MathJax 本身也有缓存机制,浏览器会缓存脚本,如果你更新了库版本,记得让用户强刷或者改一下脚本地址加个版本号参数,强制重新下载。
注意:生产环境不要长期开着
$wgCachePages = false,高并发下数据库压力会明显上升。
5. 实操:从写第一个公式到批量迁移
5.1 编辑界面里怎么写公式
配置生效后,在 Wiki 页面里就可以用<math>标签写公式了。比如写一个二次方程求根公式:
<math>x = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a}</math>保存后页面上就会渲染成标准的数学排版。行内公式和独立公式的写法略有不同,行内一般直接嵌在文字里,独立公式可以单独成段。SimpleMathJax 对这两种情况都能处理,具体取决于你用的标签属性和 MathJax 的配置。
我建议团队里统一一下写法规范,比如行内公式用\( ... \)还是<math>标签,独立公式要不要加display属性。规范统一了,后期批量处理和维护会轻松很多。我们当时定的是全部用<math>标签,行内加inline属性,这样搜索和替换都好操作。
5.2 常见 LaTeX 语法的适配情况
不是所有 LaTeX 命令都能在 MathJax 里直接用。基础的分数、根号、上下标、希腊字母、求和积分都没问题,但一些宏包特有的命令,比如\usepackage、\begin{document}这类文档级命令,在网页环境里是不支持的。表格、多行对齐这些需要amsmath环境的,要确认 MathJax 配置里启用了对应的扩展。
我整理了一份常用语法的适配对照,方便你快速判断:
| LaTeX 写法 | 是否支持 | 说明 |
|---|---|---|
\frac{a}{b} | 支持 | 分数,最常用 |
\sqrt[n]{x} | 支持 | n 次根 |
\sum_{i=1}^{n} | 支持 | 求和,上下标正常 |
\int_{a}^{b} | 支持 | 积分 |
\begin{matrix} | 支持 | 需 ams 配置 |
\begin{equation} | 部分 | 编号行为依赖配置 |
\usepackage | 不支持 | 网页环境无此概念 |
\includegraphics | 不支持 | 图片用 Wiki 原生语法 |
遇到不支持的语法,通常的替代方案是用 MathJax 支持的等价写法,或者拆成多个公式分别渲染。实在搞不定的,退回图片方案作为兜底,但要在页面上标注清楚,方便后来人维护。
5.3 批量迁移已有公式内容
如果你的 Wiki 里已经积累了大量用图片或者纯文本写的公式,迁移是个体力活。我的做法是先用站内搜索把所有包含公式特征的页面列出来,比如搜\frac、\sum这些关键词,然后逐个替换。替换时注意保留原有的上下文,别把公式周围的说明文字弄丢了。
对于图片公式,如果原始 LaTeX 源码还在(有些编辑器会存),直接复制过来就行。如果只剩图片,那就得靠 OCR 或者手动重打,这种情况建议优先处理访问量高的页面,长尾页面慢慢来。迁移过程中每改一批就抽查几个页面,确认渲染正常,别一次性全改完才发现配置有问题,回滚都来不及。
6. 踩坑实录与问题排查
6.1 公式不渲染的几种典型原因
公式显示成源码、显示成空白、或者一直转圈,是最常见的三类故障。我按排查顺序说一下思路。
第一,看浏览器控制台有没有报错。如果是脚本加载失败,多半是 MathJax 地址不通,或者被浏览器的安全策略拦了。内网环境尤其容易出这个问题,把库下到本地就能解决。第二,看页面源码里<math>标签有没有被正确输出。如果标签还在但没渲染,说明前端脚本没执行;如果标签被转义成了 HTML 实体,说明扩展没生效,回去检查wfLoadExtension那行。第三,看缓存。改完配置不清缓存,看到的还是旧页面,这个坑我踩过不止一次。
6.2 渲染样式错乱的调整
有时候公式能显示,但字体大小、行距、对齐方式不对劲。这通常是 CSS 冲突导致的。你 Wiki 的皮肤可能定义了全局的字体和行高,和 MathJax 生成的容器打架。解决办法是在皮肤的 CSS 里针对 MathJax 的容器类加一些覆盖规则,比如设置font-size和line-height为inherit或者固定值。
还有一种情况是公式和周围文字基线对不齐,行内公式特别明显。这个可以通过调整 MathJax 配置里的displayAlign和linebreaks参数来缓解,或者干脆把行内公式改成独立显示,牺牲一点排版紧凑度换取可读性。
6.3 性能与加载速度优化
MathJax 脚本体积不小,首次加载慢是客观事实。优化手段有几个:开启浏览器缓存,给脚本地址加长期缓存头;用 CDN 加速(如果外网可达);只加载需要的组件,MathJax 3 支持按需打包,把用不到的字体和扩展去掉能省不少体积。
另外,如果页面公式特别多,可以考虑延迟渲染,等页面主体加载完再处理公式。MathJax 本身有这方面的配置项,具体怎么调要看你的库版本。我的经验是,一个页面公式超过五十个,就要认真考虑性能问题了,否则用户滚动时会明显卡顿。
6.4 常见问题速查表
| 现象 | 可能原因 | 排查方向 |
|---|---|---|
| 公式显示为源码 | 扩展未加载 | 检查 wfLoadExtension |
| 公式区域空白 | 脚本加载失败 | 看控制台网络请求 |
| 一直显示加载中 | MathJax 地址不通 | 换本地库或检查网络 |
| 样式错乱 | CSS 冲突 | 检查皮肤样式覆盖 |
| 部分命令不识别 | 配置缺扩展 | 调整 MathJax config |
| 改动不生效 | 缓存未清 | 关缓存或强刷页面 |
这张表我贴在团队文档里,新人遇到问题先对照查一遍,能解决八成以上的常见故障。
7. 进阶玩法与长期维护建议
7.1 自定义宏与常用公式模板
写多了会发现有些公式反复出现,比如特定领域的符号定义、常用常数。MathJax 支持自定义宏,你可以在配置里预定义一批\newcommand,这样编辑时直接调用短命令就行,既省事又统一。比如定义一个\R代表实数集,全站通用,改的时候只改一处。
MediaWiki 这边还可以配合模板功能,把复杂公式封装成模板,调用时传参数。这对教学场景特别有用,同一类公式换个系数就能复用。不过模板嵌套太深会影响解析性能,适度使用就好。
7.2 版本升级与兼容性跟踪
SimpleMathJax 和 MathJax 库都会更新,升级前先在测试环境验证。重点看两件事:一是扩展本身和 MediaWiki 新版本的兼容性,二是 MathJax 大版本升级带来的语法变化。MathJax 2 到 3 的迁移就有不少破坏性改动,配置格式、加载方式都变了,直接升可能让现有公式集体失效。
我的做法是锁定版本,非必要不升级。真要升,先备份数据库和文件,在副本上跑一遍全站公式的渲染测试,确认没问题再切生产。测试用例可以挑几个包含复杂语法的页面,覆盖分数、矩阵、积分、多行对齐这些场景。
7.3 内容规范与团队协作
技术方案搭好只是第一步,长期可维护还得靠规范。我们当时定了几条:公式一律用<math>标签,不用其他变体;复杂公式拆成多行,每行不要太长;变量命名统一,避免同符号不同含义;公式旁边尽量加文字说明,方便搜索和非专业读者理解。
这些规范写进编辑指南,新人进来先读一遍。执行一段时间后,公式相关的维护成本明显下降,搜索命中率也上去了。说到底,工具是为人服务的,把使用习惯理顺了,技术方案的价值才能真正发挥出来。
我在多个 Wiki 项目里反复用这套方案,从最初的磕磕绊绊到后来的十分钟部署完成,最大的体会是:别一上来就追求功能最全,先跑通最小可用版本,再根据实际需求逐步加配置。公式渲染这件事,稳定比花哨重要得多。