Hexo渲染数学公式:配置方法与原理浅释

在Markdown文档编写中,我们可以使用$\LaTeX$的语法输入数学公式。常见的Markdown编辑器如Typora都可以方便地渲染数学公式。然而,Hexo在默认设置下部署博客时,并不能渲染数学公式。

原因在于,渲染数学公式需要使用MathJax(一种模块化的JavaScript框架);而Hexo的默认渲染器是hexo-renderer-marked引擎,它并不支持MathJax。

1. 更换渲染引擎

Hexo-renderer-kramed引擎在hexo-renderer-marked渲染引擎的基础上修改了一些bug(“kram”这个名字目测是把“mark”倒过来拼写得到的),支持了MathJax。因此,将渲染引擎改为Hexo-renderer-kramed可以解决数学公式渲染的问题

使用Git Bash打开自己的博客文件夹,输入下面的代码即可卸载marked、安装kramed:

npm uninstall hexo-renderer-marked --save
npm install hexo-renderer-kramed --save

中国用户还可使用cnpm命令代替npm,获得更好体验。cnpm命令的安装可自行搜索,这里不再赘述。

更换渲染引擎之后,还需要相应地更改一些其他配置。

2. 修改配置文件

诸多网文混淆了站点配置文件主题配置文件。二者的文件名都是_config.yml,但文件的位置不同。站点配置文件位于博客的根目录,即.\_config.yml,管理的是整个博客的全局设置;而主题配置文件则位于对应的主题文件夹之下,例如Ocean主题的配置文件路径为.\themes\ocean\_config.yml,管理的是与该主题的相关的具体设置。

更换kramed引擎之后,需要在主题配置文件中增加这样几行:

mathjax:
  enable: true
  cdn: https://cdn.jsdelivr.net/npm/mathjax@2.7.8/MathJax.js?config=TeX-AMS-MML_HTMLorMML

还要在站点配置文件中增加:

math:
  engine: 'mathjax'
  mathjax:
    src: custom_mathjax_source

3. 添加JavaScript脚本

根据一些网文的说法,如果博客使用的是Next主题,那么至此已经可以实现行间公式的渲染。不过,对于有些并未引入MathJax功能的主题,还需要自行增加JavaScript脚本文件。

Ocean主题为例。首先打开.\themes\ocean\layout文件夹,如果该目录下没有mathjax.ejs文件,则新建 mathjax.ejs文件。可以先新建一个txt文本文档,将后缀更改为.ejs即可。

mathjax.ejs中写入如下内容:

<% if (theme.mathjax.enable){ %>
    <script type="text/x-mathjax-config">
      MathJax.Hub.Config({
        tex2jax: {
          inlineMath: [ ['$','$'], ["\\(","\\)"] ],
          processEscapes: true,
          skipTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code']
        }
      });
      MathJax.Hub.Queue(function() {
        var all = MathJax.Hub.getAllJax(), i;
        for(i=0; i < all.length; i += 1) {
            all[i].SourceElement().parentNode.className += ' has-jax';
        }
      });
    </script>
    <script type="text/javascript" src="<%- theme.mathjax.cdn %>"></script>
    <% } %>

此外,还要在.\themes\ocean\layout\post.ejs中追加如下代码:

<% if (theme.mathjax){ %>
  <%- partial('mathjax') %>
<% } %>

4. 打开MathJax开关

如果仍未能正确渲染数学公式,那么可能还需在Markdown文档的顶部配置里打开MathJax开关,即mathjax: true

---
title: 文章标题
date: 2021-01-01 00:00:00
mathjax: true
---

$$
e ^ {i\pi} + 1 = 0
$$

即可显示:

这行设置需要在每一篇用到数学公式的文章中手动添加。不添加这行设置,文章就不会加载MathJax,以提高页面渲染的速度。

5. 调整行内渲染设置

Markdown的公式分为行间公式行内公式。经过上述配置之后,行间公式已经可以正确渲染。但行内公式仍有可能渲染错误。

原因在于Markdown语法和$\LaTeX$数学公式语法有一定冲突。例如,用两个下划线_或两个星号*包围的文字,在Markdown语法中都被识别为斜体;但在$\LaTeX$​​数学公式中,下划线_用于表示下标。因此,当同一个行内公式中出现两个下标时,下划线_会被转化为<em>标签,二者之间的部分就会被渲染为斜体。这就造成了混乱。此外,反斜杠 \花括号 {}也有可能造成渲染错误。

为此,需要更改与行内渲染相关的JavaScript脚本。回到博客根目录,打开.\node_modules\kramed\lib\rules\inline.js(注意打开node_modules文件夹之后选择的是kramed文件夹,而不是hexo-renderer-kramed文件夹),找到escape变量,注释掉原来的值,并更新:

// escape: /^\\([\\`*{}\[\]()#$+\-.!_>])/,
  escape: /^\\([`*\[\]()#$+\-.!_>])/,

这样就取消了对反斜杠和花括号的转义(escape)

再找到em变量,注释掉原来的值,并更新:

// em: /^\b_((?:__|[\s\S])+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
  em: /^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,

这样就停用了以下划线标注斜体/强调(em)的语法

此时,形如

$N = P_1 ^ {\alpha _1} P_2 ^ {\alpha _2} \cdots P_n ^ {\alpha _n}$,其中$P_1 < P_2 < \cdots < P_n$,$P_1 , P_2 , \cdots , P_n$为质数。

这样的复杂行内公式,就可以正确渲染了:$N = P_1 ^ {\alpha _1} P_2 ^ {\alpha _2} \cdots P_n ^ {\alpha _n}$,其中$P_1 < P_2 < \cdots < P_n$,$P_1 , P_2 , \cdots , P_n$为质数。

当然,调整行内渲染设置之后,在Markdown文档中就要用星号*标注斜体文字,因为用下划线_标注斜体的语法已经被停用了。

参考文献