抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

今日、海を見た。もう怖くない

随着 Hexo 的发展,有越来越多的小伙伴使用 Hexo 搭建技术博客,其中不乏大量包含数学公式的场合:为了满足这种需要,一般是在 Markdown 中使用部分LaTeX\LaTeX 语法书写数学公式,Hexo 也支持使用 Mathjax 渲染这些公式;本是一件很舒服的事情,但是当编写多行公式的时候,会发现渲染出现错误:

比如下面的数学公式:

1
2
3
4
5
6
7
8
9
10
11
12
13
\begin{bmatrix}
1 & x_0 & x_0^2 & \cdots & x_0^n \\
1 & x_1 & x_1^2 & \cdots & x_1^n \\
\vdots & \vdots & \vdots & \ddots & \vdots \\
1 & x_n & x_n^2 & \cdots & x_n^n
\end{bmatrix}
\begin{bmatrix}
a_0 \\ a_1 \\ \vdots \\ a_n
\end{bmatrix}
=
\begin{bmatrix}
y_0 \\ y_1 \\ \vdots \\ y_n
\end{bmatrix}

是一个和范德蒙矩阵相关的算式;它正确渲染后的样子应该是这样的:

[1x0x02x0n1x1x12x1n1xnxn2xnn][a0a1an]=[y0y1yn]\begin{bmatrix} 1 & x_0 & x_0^2 & \cdots & x_0^n \\ 1 & x_1 & x_1^2 & \cdots & x_1^n \\ \vdots & \vdots & \vdots & \ddots & \vdots \\ 1 & x_n & x_n^2 & \cdots & x_n^n \end{bmatrix} \begin{bmatrix} a_0 \\ a_1 \\ \vdots \\ a_n \end{bmatrix} = \begin{bmatrix} y_0 \\ y_1 \\ \vdots \\ y_n \end{bmatrix}

但是实际上,渲染出来的结果很有可能是下面这样……

[1x0x02x0n 1x1x12x1n  1xnxn2xnn][a0 a1  an]=[y0 y1  yn]\begin{bmatrix} 1 & x_0 & x_0^2 & \cdots & x_0^n \ 1 & x_1 & x_1^2 & \cdots & x_1^n \ \vdots & \vdots & \vdots & \ddots & \vdots \ 1 & x_n & x_n^2 & \cdots & x_n^n \end{bmatrix} \begin{bmatrix} a_0 \ a_1 \ \vdots \ a_n \end{bmatrix} = \begin{bmatrix} y_0 \ y_1 \ \vdots \ y_n \end{bmatrix}

实在是让人头秃(

原因分析

一般而言,Hexo 是将公式块(被 $ 或者 $$ 包裹)渲染为一个元素,然后在页面中加载 MathJaxJS 文件。浏览器加载页面的时候运行 MathJax 读取这些元素中的公式并渲染完成替换的。但是 marked 会优先转义 Markdown 的语法,再考虑数学公式;因此当两者的语法冲突的时候,就会使得最终提供给 MathJax 的公式出现异常,导致渲染错误。

解决方法

既然已经确定了渲染不正确的原因是 hexo-renderer-marked 的原因,那么就可以从这方面入手考虑解决方法了:

修改 Marked.js 源码

因为 Marked.js 会先将下划线 escape 成 <em>,将LaTeXLaTeX 中用于换行的 \\ 转移成 \,使得客户端的 MathJax 在渲染的时候无法正确读取公式导致渲染异常。因此,可以修改 nodes_modules 中的 marked 的源代码,或自行发布一个私有的 marked 作为依赖。

marked/lib/marked.js 中:

  • 去掉 \ 的额外转义
  • em 标签对应的符号中,去掉 _ :因为 markdown 中有 * 可以表示斜体

修改方式如下:

首先删除对于反斜杠的转义

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

再删除对于 em 的多余的转义:

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

遗憾地是上述做法并不适合目前较新版本的 Marked.js,所以现在无法使用。如果有新版本的 Marked.js 的修改方法也欢迎分享(

强行适配渲染规则

因为主要受到影响的是多行公式,所以在需要使用多行公式的时候,使用 \\\\ 替换 \\,就可以保证页面的正常渲染。

但是这样并不能解决关于下划线的渲染问题还是得看脸,而且这样在 Typora 等可以正确渲染的本地环境下,看到的公式之间会有莫名的空行(并不优雅)。

更换渲染引擎(推荐)

既然是渲染引擎的锅,那么换一个渲染引擎就好了;下面列举作者尝试过的一些渲染引擎,并简单介绍一下它们的优缺点:

作者的博客在进行这些尝试的时候使用的主题是 Volantis4.3.1 版本。

hexo-renderer-markdown-it

和 Hexo 默认使用的 hexo-renderer-marked 不同,在博文的 front-matter 不开启 mathjax 的情况下,似乎会先使用 Katex 渲染公式。Katex 会在生成静态文件的过程中就完成数学公式的渲染,而不是在客户端的浏览器中。因此可以获得很棒的加载速度(毕竟在一些性能较差的设备上经常能看见公式半天加载不出来的情况);但是问题在于 katex 支持的LaTeX\LaTeX 实在是太少了,远远不够用。

一些 Hexo 主题的作者也会基于这个渲染器进行一些修改,作者就没有一一尝试了。

hexo-renderer-kramed

这是一个 hexo-renderer-marked 的分支,也会和 hexo-renderer-marked 产生冲突。本来应该是一个很不错的选项,但是作者在使用它作为渲染引擎后运行 gulp 来最小化 HTML 会报错…… 所以就没有继续尝试了。

hexo-renderer-syzoj-renderer

大佬 Menci 出品,使用她为开源的 SYZOJ 写的 Markdown 渲染器来渲染文章的插件其实使用的还是 markdown-it。优点是渲染行为和 marked 非常类似,且对于 MathJax 的支持和语法容忍度非常高:基本上是你在 Typora 里写的什么样,Hexo 渲染出来的就是什么样特别是对于 SYZOJ 用户,可以说是非常的舒服。

而且,虽然此渲染器使用 MathJax 渲染,但是这个过程也是在后端完成的。因此客户端浏览器的公式渲染速度很快,解决了 MathJax 公式渲染阻塞留白的问题。

但它的缺点就是在某些兼容性上可能有一些微妙的问题:博主使用的主题 Volantis 在开启 Pjax 后,包含数学公式的页面的 Pjax 跳转会失效(Issue #621 · volantis-x/hexo-theme-volantis);此外,很多主题会对于配置文件里写的字符串在布局的 ejs 文件中调用 markdown() 接口渲染成 Markdown,这个渲染器没有提供这个接口(也就是说,还得保留 hexo-rederer-marked 才可以使得这些部分正常渲染)。

如果你使用的博客主题没有 Pjax,或者和这个渲染器没有什么冲突的话,还是非常推荐使用的。

hexo-renderer-pandoc

目前本博客采取的方案,在客户端使用 mathjax 渲染数学公式。相比于上面的 hexo-renderer-syzoj-renderer 容忍性较差:用来包裹行内公式的 $ 和公式之间不能有空格,否则会渲染失败。此外,它对于一些其他的元素的渲染行为和 hexo-renderer-marked 的行为不同:比如对于 Markdown 图片 ![alt](url),它们的渲染结果分别如下:

使用 hexo-renderer-marked 的渲染结果:

1
<img alt src="url"> 

使用 hexo-renderer-pandoc 的渲染结果:

1
2
3
4
5
6
<figure>
<img alt src="url">
<figcaption>
alt
</figcaption>
</figure>

此外,另一个显著的问题是:直接写在文本中的链接(没有被 < >[]() 包裹的链接)不会被自动加上超链接。

不过这对于 Volantis 而言倒也问题不大:上面这个问题实际上只要外挂一个 CSS 就没什么问题了;至少这样的渲染并不会影响到 z-lazyload 的脚本对于图片的标记。但是对于其他的,按照 hexo-renderer-marked 的渲染行为而设计主题,就可能会产生兼容性问题。

并且,使用 hexo-renderer-pandoc 可以正常渲染配置文件中那些使用 markdown() 渲染的字符串。因此真的可以卸载 hexo-renderer-marked 了!

另一个问题就是 hexo-renderer-pandoc 的渲染需要依赖本地的 pandocPandoc - Installing pandoc;如果只是本机生成静态文件再上传到服务器 / Github Page 倒也不算是什么问题了,但是如果我们使用自动部署,就需要对原来的部署脚本进行一些修改:

Github Action

在编译和部署的命令之前增加安装 Pandoc 的命令:

1
2
3
4
+ - name: Setup Pandoc
+ run: |
+ wget https://github.com/jgm/pandoc/releases/download/2.10.1/pandoc-2.10.1-1-amd64.deb
+ sudo dpkg -i pandoc-2.10.1-1-amd64.deb
Vercel

目前无法在 Vercel 上安装 Pandoc,所以无法使用 Vercel 来构建博客的静态文件。

总结

  • hexo-renderer-syzoj-renderer:如果对 Pjax 没有什么要求,且使用的主题没有兼容性的问题;
  • hexo-renderer-pandoc:兼容性略好,但是无法使用 Vercel 自动构建静态页面;

Volantis 的社区里也确实没有说这个相关的文章啊,还是写一篇好了(

虽然但是,还是希望能够解决这个兼容性问题能用上 hexo-renderer-syzoj-renderer 啊()用 Pandoc 渲染在各种方面还是有点麻烦了==

评论