Find the Formula Rendering Issue of Hugo
April 18, 2025 · 7 min read · Page View:
A record of the formula rendering issue of Hugo
If you have any questions, feel free to comment below. And if you think it's helpful to you, just click on the ads which can support this site. Thanks!
最近在写数学相关的博客的时候,发现 mathjax
的渲染效果不尽人意,经常会在渲染的时候出现非预期的渲染效果,虽然只是极个别的公式,对整体影响并不大,当然我可以放着不解决,继续进行接下来的事情,或者简单将公式通过截图的形式放在文章里,也无伤大雅。但是凡事得想长远,想本质,今天大可以为了这一个公式采用省事的方法解决,那么它不会是我写的最后一个公式,未来可能还会有更多,因为这是一个长期的问题,如果放任不管,可能下次遇到类似的问题,我可能还是会采用省事的方法解决,那么这个问题就会一直存在,所以我认为放下手头的事,集中解决这个问题是值得的。
问题的判断 #
我分析了一下这个问题:
$$
\begin{align*}
R_{xx}(t_1,t_2) &= E\{X(t_1)X^{*}(t_2)\} \\
&= \iint x_1x_2^{*} f_x(x_1,x_2,\tau = t_1 - t_2)dx_1dx_2\\
&= R_{xx}(t_1 - t_2)=\hat{R}_{xx}(\tau)=R_{xx}^{*}(-\tau),
\end{align*}
$$
公式本身的问题 #
这是 markdown 中常见的 multi-line 公式,在线的环境下是完全可以渲染的,因此可以排除公式本身的错误。
公式渲染库的问题 #
那么问题只可能出现在 Hugo 对于数学公式的渲染上,我的 hugo 采用的是 mathjax 作为渲染库,因此我自然而然得怀疑是否是 mathjax 导致的问题,因此我首先验证了在 mathjax 中是否可以采用 \begin{align*}
的环境来渲染公式,答案是肯定的。
页面解析的问题 #
既然环境没有问题,那么问题大概率出现在 chrome 对于页面的解析上,在常见的 markdown 文档到 html 的解析中,按照以往的经验来看,最容易出现问的是几个特殊的字符,比如 *
和 _
。因此我将目光转向了这两个字符。
转义 #
我最先搜索了是否有和我类似的情况,但是解决方案全部是转义,但是我的预期是公式必须要保证和原生的 latex 公式保持一致,因此使用转义的方式我排除了(因为我想大概率这也会是一个长期的问题,如果每次都通过转义的方式来实现,那么之后一直会通过转义的方式实现,这为今后 markdown 文档的通用处理与迁移制造了很大的麻烦)。
切换公式渲染库 #
通过查找,我看到 hugo 的 issue 中有人说轻量型的 Katex 引擎可以解决这个问题,因此我将博客原本的 mathjax 引擎替换为了 Katex 引擎,结果令人失望,问题依然存在。但也不是完全没有收获,通过比较不难发现,在禁止浏览器的 cache 的情况下,Katex 的渲染速度大概是 mathjax 速度的 5 倍,这意味着在一个完全没有 CDN cache 命中的情况下(详见我的 另一篇文章),一个完全陌生的用户访问我的博客时能够获得更好的体验,因此我决定今后博客的公式渲染全部基于 Katex。
替换表达 #
寻根溯源,我对可能引起问题的符号进行了替换尝试,我通过查询 katex 的文档[1],将 *
替换为 \ast
,在最小单元中显示成功了。但是整体的公式并未成功渲染,我通过 inspect 查看 html 发现,_
符号被解析成了 <em>
标签。
因此这时我想到的是写一个 javascript 的正则匹配,在 katex 渲染之前将公式中的 _
符号进行替换,当然这样是完全不够的,还需要同时替换解决 {}
以及 \\
符号的问题。
在我开始着手之前,确保没有重复解决问题是必须要考虑的,因此我再次搜索相关的问题,无论是 stackoverflow 亦或是 issue,大部分人都使用转义的方法,有少部分人为了防止公式被解析,将公式放在单独的 <div>
里。我再次在官方文档里一条一条文档参数寻找,最终我追溯到了[2] hugo 的 Goldmark Extensions: Passthrough[3] 参数,发现一年多以前官方就注意到了这个问题,通过分界符的判断将公式环境下的内容不进行解析。然而即使是最近的众多第三方主题 issue 里,大部分人仍分享自己采用转义的方式,因此保证对问题的分析和信息的检索是很有必要的。
最终解决方案 #
添加配置项
[markup.goldmark.extensions.passthrough]
enable = true
[markup.goldmark.extensions.passthrough.delimiters]
block = [['\[', '\]'], ['$$', '$$']]
inline = [['\(', '\)']]
同时我也将信息同步在了 stackoverflow 中。
最终,无需任何调整与转义,latex 中的公式在浏览器中不会进行 markdown 的解析,而是直接通过 katex 进行渲染。
$$ \begin{align*} R_{xx}(t_1,t_2) &= E\{X(t_1)X^{\ast}(t_2)\} \\ &= \iint x_1x_2^{\ast} f_x(x_1,x_2,\tau = t_1 - t_2)dx_1dx_2\\ &= R_{xx}(t_1 - t_2)=\hat{R}_{xx}(\tau)=R_{xx}^{\ast}(-\tau), \end{align*} $$如果你采用 workflows 的方式部署,记得确保流程里的 hugo 版本与本地保持一致,以免出现本地渲染结果与线上不一致的情况。
References
Related readings
If you want to follow my updates, or have a coffee chat with me, feel free to connect with me: