为Hexo寻找一个合适的Latex方案

2020-07-17 更新: 现在推荐所有使用Hexo的人都采用hexo-filter-mathjax插件,支持unicode字符,后端渲染,前端不用外挂CSS样式表和Javascript脚本。如果想找解决方案可以直接阅读"Maxjax 后端渲染"小节。


许许多多的Hexo博客都是Next主题的,而这样的大主题大多封装了Mathjax或Katex等前端渲染工具,配置比较简单。我这个小主题就只能在网上找解决方案了。

现在来总结,其实无非就是两个大方向,前端渲染和后端渲染。其中前端渲染是主流,而绝大部分前端渲染都是用的Mathjax或Katex。

前端渲染的流程是,在生成时Hexo渲染器(如hexo-renderer-pandoc)首先一轮渲染把Markdown中的Latex代码转换为Mathjax或Katex支持的代码,然后在前端加载html时前端的Javascript脚本就会渲染Latex公式为矢量图形。

可我最后用的是后端渲染,虽然说后端渲染有种种好处,但是我用它的最开始的原因却是我不能把前端渲染的方案做完善,也就是说,我前端方面是个菜狗。

使用hexo-math

看见不少博客推荐使用hexo-math,于是我也就去用了,hexo-math应该也是对Mathjax和Katex的包装,我安装了hexo-math后,配置了一下博客的配置文件,先试了试Mathjax,不知道什么原因(因为我菜),我只能使用2.7大版本的Mathjax才能正常渲染,而3.0版本的不能工作,似乎是js cdn地址有问题。

然后就凑合着用2.7版本吧,也能用,但是有一个我受不了的地方,那就是加载脚本时不知道为什么(又显示了我前端菜狗的本质)总会有一个灰色的加载标签,如下图。

Figure 1: 加载时的标签

我自然是受不了这个的,于是又换用Katex,但是hexo-math貌似完全对渲染不感冒。故只能更换方法。

DIY流

这个DIY指的就是不用别的工具封装Mathjax和Katex,而直接自己来把这两个工具(之一)整合进自己的主题里,我主要是学习了这篇博文,修改了ejs模板文件加载Katex的js脚本和css样式表,然后再使用自动渲染脚本在加载完成时渲染。

听起来好像没有问题,但是问题就在于我太菜了。虽然可以在主页完成前端渲染,但是只要点链接到文章页面,公式又不渲染了。问题就出在“在加载完成时渲染”,虽然可以自己选择时机调用渲染函数,但是我也只会一个HTML的DOM结构加载好后渲染和onload渲染。输入任一个有公式的网址并刷新,渲染都没有问题。但是当点击链接跳到另一个有公式的页面时,不会触发渲染函数的调用,我也不知道应该监听哪个事件或怎样做才能让通过链接跳转也可以渲染。

(随着后来我对这个主题的改动越来越多,了解越来越深,我知道是原主题采用的一个替代instantclick的脚本,会在点击其他本站连接的时候不进行完整的html重新加载,监听html onload之类的事件会失效)

这时候我还没有向我的菜屈服,于是我去学习我用的Theme的开发者的做法,这位仁兄没有在主题中适配公式,但是他的blog有一大堆公式,我就去看看他的html页面是怎么搞的脚本。结果一去发现,他连渲染脚本都没有,因为他用的是后端渲染!

懒惰的我于是也去找后端渲染的方法了,并给自己找了后端渲染的合理性理由。

Katex 后端渲染

后端渲染参考了这篇文章这篇文章,其中后者2018年还是位高中生,也是打OI的。我打OI时也就玩玩泥巴纯粹学学算法,而厉害的同学学算法的时候不忘全面发展,都会用Latex和Hexo了。如果他2018年高一的话,现在应该要高考了,祝他顺利吧。

后端渲染主要是hexo-katex结合pandoc用来输出,但是hexo-katex已经停止维护了,或许会存在安全漏洞(但是这博客是静态网站,出现安全漏洞和我又有什么关系呢)。

Mathjax 后端渲染

这部分是我2020年7月份更新的。

在使用Katex后端渲染时,我就在思考,为什么我不使用Mathjax后端渲染呢?

hexo-katex有几大缺点:

  1. 不支持unicode字符如中文字符
  2. 停止维护
  3. 如果行间公式过长,移动端显示会截断

况且Mathjax 3.0之后的渲染速度已经不弱于Katex,说到底,我没有去寻找Mathjax后端渲染的方法的原因是没有很强的推动力去驱使我换Latex插件,之前的方案已经可以凑合了。

但是一次偶然的机会,我发现了一个使用Mathjax后端渲染的插件hexo-filter-mathjax,这个插件比较新而年轻,于是我毫不犹豫地去尝试更换了这个插件。

先推荐一波这个插件,只要使用使用这个插件,一键安装,别的地方都不用改(记得把renderer改成hexo-renderer-pandoc)。我还给该插件贡献了几行代码。如果你已将安装了其他的Latex插件,你需要卸载它们。

使用Mathjax后端渲染的原理很简单,渲染器pandoc会先将Markdown文件中的Latex代码渲染成Mathjax代码,而后使用Mathjax官方提供的Node.js的库将Mathjax代码渲染成SVG嵌入html即可。

看到有SVG文件,你是不是觉得会有很多的图片传输,影响网络体验?其实不然,如果你使用浏览器的开发工具查看下面的公式,那么你会发现它的html代码是如Figure 2所示。

Figure 2: 公式的SVG代码

这里的数字是什么呢?很显然,我们知道SVG图片是矢量图,而矢量图是可以用B样条等数学曲线表示的,这里的数字应该就是描述这些曲线的数字。

那么,在html中内联这么多数字会不会让html大小过大呢?我认为,如果你不是要写一篇有成千上百个公式的文章的话,你完全不用考虑这个问题。

从理论上来说,后端渲染Katex时是传入字符,然后用CSS样式表渲染,有很多公式时只增加了原字符,那么的确使用后端Katex的方案会加载更小的资源。但是如果你看一看使用Katex样式表时的样式表大小和字体文件的大小,你就会知道一个字体文件都有上百KB,而Katex往往不只引入一个字体文件,因此在公式较少时使用Mathjax后端渲染是完全不会在资源大小上输给Katex的。

不过我有种感觉,使用该插件渲染时得到的公式内字母大小似乎略大于我的正文字体大小。这大概是为数不多的缺点吧。另外,由于Hexo自己的一个bug,hexo-filter-mathjax会在html文件中内联两次SVG的样式表(很小的样式),我自己fork的仓库修改了一下避免了这个bug,但是原仓库的主人准备等Hexo 5.0发布来修复这个bug。

总结

更新后下面说的部分地方已经不再成立,但是$前后的空格依然是问题

总而言之,这些方法都不是尽善尽美,我理想中的Latex渲染至少应该做到和Typora一样(或许Typora用的Mathjax),能够处理unicode字符,$符号前后有没有空格都能正常显示。