NexT主题内部已经集成了MathJax(其实还有KaTeX),相对而言,配置起来已经是很方便了。但在一些细节方面上还是存在各种小问题,这里记录一下解决方式。
版本信息:
Node.js: 11.10.1
Hexo: 3.8.0
NexT: 7.0.1
1. 启用MathJax
编辑themes/next/_config.yml
,找到math
配置项,配置为enable: true
即可启用。
至于其余配置项:
per_page
默认为true
,表示每篇文章需要额外地单独启用MathJax。也就是说,还需要在其文章头部添加mathjax: true
,否则其中的MathJax公式依然不会被解析。engine
默认使用的是mathjax
,相比katex
功能更强大(当然也慢了一点)
2. 渲染引擎问题
启用完成后,应该就已经能在文章中写一些简单的MathJax了。
但是!
Hexo默认的渲染引擎hexo-renderer-marked
对MathJax的支持很不好,会出现各种莫名其妙的问题。NexT主题的官方文档也推荐换用其他渲染引擎。
推荐换用hexo-renderer-kramed
。
npm un hexo-renderer-marked --save |
3. MathJax符号与Markdown符号冲突问题
hexo-renderer-kramed
也不是尽善尽美,它还是有一点问题:行内公式中MathJax符号与Markdown符号冲突。
比如下面这几个行内公式:
$ \alpha\beta $ |
会发现前两个都正常,成功地渲染出了α(正常β)
和α(下标β)
,但是第三个公式,结果却并没有解析成公式,显示是这样的:
$ \alpha\beta = \gamma\delta $
检查元素源码发现是这样的:
<p>$ \alpha<em>\beta = \gamma</em>\delta $</p> |
原来这里两个下划线_
并没有被识别为公式中的下标符号,而是被识别为Markdown的斜体符号去了,最后也就生成了<em></em>
。
治标不治本的解决方法是:放弃行内公式,一律使用块公式。这种方法的弊端就不说了。
更好地解决方法是直接修改hexo-renderer-kramed
相关源码,位于文件node_modules/kramed/lib/rules/inline.js
。
将其中的
var inline = { |
修改为
var inline = { |
偷懒的话用下面这条sed
命令可以直接完成上述修改
sed -i -e 's|escape: /^\\\\(\[\\\\`\*{}\\\[\\\]()#$+\\-.!_>\])/|escape: /^\\\\(\[`\*\\\[\\\]()#$+\\-.!_>\])/|' -e 's|em: /^\\b_((?:__\|\[\\s\\S])+?)_\\b\|^\\\*((?:\\\*\\\*\|\[\\s\\S\])+?)\\\*(?!\\\*)/|em: /^\\\*((?:\\\*\\\*\|\[\\s\\S\])+?)\\\*(?!\\\*)/|' node_modules/kramed/lib/rules/inline.js |
重新生成一次,问题解决。
4. 长公式超出范围问题
如果使用的是2019年3月12日后新版本的NexT,这一节内容可以不用看了,因为这些问题已经被我提交PR修正过了。参考pull request #669。
NexT主题对公式的样式配置存在一些小问题,导致当公式太长的时候,显示内容会超出文章区域。
比如考虑下面这一段代码中的MathJax公式代码,其中第一段定义了一些宏,第二段是一个行内公式,第三段是一个长公式。
$$ |
然后来看显示效果,这两张展示图中,上半部是NexT默认配置的显示效果,下部是修改后的效果展示。两幅图的差别在于使用了不同的MathJax渲染设置,效果也不一样。
可以发现,如果使用默认的配置,行内公式超长时总会超出文章显示区域,而块公式时候会超出区域则取决于选择的渲染方式(HTML-CSS不会超出区域,其他情况会)。
进行了修改后,总是会在公式超出区域时生成滚动条,效果更佳理想。
究其原因还是要看NexT源码中的layout/_third-party/math/mathjax.swig文件,重点是尾部的一些代码。
<script type="text/x-mathjax-config"> |
上面JS代码为每个MathJax公式的源节点(对应markdown中的公式,也对应生成的html中的<script type="math/tex;"></script>
节点)的父节点添加has-jax
类。但是这有两个问题:其一,这个has-jax
类在整个主题中根本没有用到过,也就说没有用处了;其二,从其本意来说,MathJax源节点的父节点不一定也是MathJax显示节点(所有MathJax图形都在其中绘制)的父节点。如下所示对于块公式的情况,MathJax会为显示节点生成一个额外的包裹容器。
graph TB ip(行内公式父节点) --> if(显示节点) ip --> is(源节点$...$) dp(块公式父节点) --> dc(显示节点容器) dc --> df(显示节点) dp --> ds(源节点$$...$$)
然后看最下面的CSS代码,它配置了MathJax_Display
类样式,使之在超出区域时生成滚动条,然而,MathJax_Display
仅仅对应了使用特定MathJax渲染方式的块公式,也就无怪乎为何行公式总是会超出区域。
因此,解决方法如下:
|
三处修改:
- 利用显示节点的id正好是源节点id后面加上’-Frame’,找到显示节点再获得它的父节点,为其添加
has-jax
类标记 - 将CSS中的选择符改为
.has-jax
,匹配所有jax-jax
类的元素 - 将
scroll
改为auto
,效果更好点,后者会避免在没有超出区域时也显示一个空白滚动条
5. overflow属性导致列表前缀符丢失问题
这是由于上一步修改导致的次生问题,使用未经修改的NexT主题也不会有这个问题。
而且与其说这是个配置问题,更像是浏览器的bug,参考这个stackoverflow问题。
表现形式为,当一个(有序/无序)列表项中的内部元素或者其本身具有overflow属性时,列表项前缀的项目符号不能正常显示(鬼知道为什么会这样)。比如考虑如下的MarkDown代码:
- $\alpha$ |
如果采纳了上一步的提到的修改,为了避免超出区域为公式显示节点的父节点添加了overflow属性,显示效果将会是这样的:
公式倒是没有问题,但会发现列表项前面的小点没了!
具体参考#669和#745中的讨论,总之目前没有比较好的解决办法。
一个很难看的解决思路是:强制将列表的前缀符号位置设置到元素内部,css如下:
li { |
但是这种方法并不是很好,因为副作用比较大,会导致在一些情况下前缀符和列表中文字内容断成两行,显示效果就比较难看了。