最近在复习万恶的数值分析,原本手抄的公式笔记阅读起来不太方便,打算用 MathJax 重写公式再整理到 Hexo 博客上。奇怪的是,公式在本地 Typora 上渲染的完全 OK,搬 Hexo 上咋就出问题了?(重启喝水都试过了

例如e_r(x^{*})=\frac{x-x^*}{x^*},讲道理它应该长成下面的样子:

然而实际上它是这个样子:$e_r(x^{})=\frac{x-x^}{x^*}$

又或者f_n=f_{n-1}+f_{n-2},讲道理它应该生的和下边一样俊俏:

然而也不幸长残了:$f_n=f_{n-1}+f_{n-2}$

What are you 弄啥嘞?😡

注:针对两个单独的_的语义冲突已在后文中修复,因此上面的行内公式显示正常。未修复之前,Markdown 渲染器仍然会将两个单独的_之间的内容渲染为<em>标签,显示效果为:$fn=f{n-1}+f_{n-2}$

问题分析

不难发现,上边既然有成功的渲染,就说明 MathJax 本身没有罢工。而且,仔细观察还会发现,第一个公式中最开始两个*中间的字体变成了斜体;第二个公式中最开始两个_也是同样的情况。审查元素发现,第一个公式中的斜体部分被渲染成了<em>标签:

<em>})=\frac{x-x^</em>

这样来看答案就很清楚了:这个错误是由 Markdown 渲染器(默认的是 hexo-renderer-marked )引起的。Markdown 本身并不支持 Latex在渲染时正则匹配到两个_*就会把下划线替换成了<em>,于是到了 MathJax 渲染公式时就彻底懵了。

解决办法也很简单:使用 hexo-renderer-kramed 替换 Hexo 默认的渲染器 hexo-renderer-marked

替换默认渲染引擎

hexo-renderer-kramedhexo-renderer-markedFork 修改版,仅针对 MathJax 渲染的语义冲突问题进行了修改,因此可以放心使用。在 Hexo 根目录下执行以下命令替换默认渲染引擎:

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

更换渲染引擎后,整行公式就可以正常显示了,然而行内公式还是会遇到<em>标签语义冲突的问题。在 Markdown 语法中,用$$包括起来的内容表示整行公式,用$包括起来的内容表示行内公式。之所以行内公式的渲染依然存在问题,是因为 hexo-renderer-kramed 引擎同样存在语义冲突的问题。

解决语义冲突

在博客根目录下,找到node_modules/kramed/lib/rules/inline.js文件,在inline变量中做出如下修改:

var inline = {
  // escape: /^\\([\\`*{}\[\]()#$+\-.!_>])/, 第 11 行, 将其修改为
  escape: /^\\([`*\[\]()#$+\-.!_>])/,
  autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
  url: noop,
  html: /^<!--[\s\S]*?-->|^<(\w+(?!:\/|[^\w\s@]*@)\b)*?(?:"[^"]*"|'[^']*'|[^'">])*?>([\s\S]*?)?<\/\1>|^<(\w+(?!:\/|[^\w\s@]*@)\b)(?:"[^"]*"|'[^']*'|[^'">])*?>/,
  link: /^!?\[(inside)\]\(href\)/,
  reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
  nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
  reffn: /^!?\[\^(inside)\]/,
  strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
  // em: /^\b_((?:__|[\s\S])+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/, 第 20 行,将其修改为 
  em: /^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
  code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
  br: /^ {2,}\n(?!\s*$)/,
  del: noop,
  text: /^[\s\S]+?(?=[\\<!\[_*`$]| {2,}\n|$)/,
  math: /^\$\$\s*([\s\S]*?[^\$])\s*\$\$(?!\$)/,
};

第 11 行的修改去掉了\\{},目的是在原基础上去掉对\{}的转义 (escape)。

第 20 行的修改去掉了\b_((?:__|[\s\S])+?)_\b,目的是去掉对两个_之间内容的<em>标签转义。

也就是说,依然可以在 Hexo 中使用*表示斜体,但_表示_斜体_就不会生效了

另外在行内公式中,针对两个*的语义冲突依旧存在,目前来看没什么比较好的解决办法(摊手)。

按需加载 MathJax

hexo-theme-indigo 默认集成了 MathJax,然而只在主题配置文件中定义了 MathJax 的开关。这样就会造成一个问题:

只要theme.mathjaxtrue,所有文章页面都会引入 MathJax.js,在不需要使用 MathJax 的页面中会带来毫无必要的时间和资源开销

因此需要修改主题模板文件,使其按需加载 MathJax.js

还是以 hexo-theme-indigo 为例,首先在主题配置文件theme/_config.yaml中,将MathJax设置为true

mathjax: true

随后修改主题模板文件中的判定条件。以 hexo-theme-indigo 为例,其判定是否引入MathJax.js的代码在layout/_partial/plugins/mathjax.ejs文件中:

<% if (theme.mathjax){ %>
<!-- mathjax config similar to math.stackexchange -->

<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 async src="//cdn.bootcss.com/mathjax/2.7.5/MathJax.js?config=TeX-MML-AM_CHTML" async></script>
<% } %>

显然,只需修改第一行的判定条件为双重判定:

<% if ( theme.mathjax && page.mathjax ){ %>

最后在需要使用 MathJax 文章的 Front-matter 中,将Mathjax设置为true,即可在该页面中引入 MathJax.js 而不影响其他页面

---
title: 测试Mathjax
category:
  - 前端
tags:
  - Hexo
  - MathJax
date: 2018-10-29 19:58:35
mathjax: true
---

这里还有一个小问题:博客的首页也可能会调用 MathJax.js 渲染公式,而按照以上设置,非文章页面是不会引入 MathJax.js 的。这里给出两种解决办法:

  1. 合理设置<!-- more -->标签位置,确保首页不会展示公式
  2. 修改mathjax.ejs的判定条件如下,首页同样引入 MathJax.js
<% if ( theme.mathjax && ( page.mathjax || is_home() ) ){ %>

问题解决~最后来测试一下😎

参考文章

  1. 常用数学符号的 LaTex 表示方法
  2. 一份不太简短的 LaTex 2 介绍.pdf
  3. Online LaTex Equation Editor | CODECOGS
  4. 在 Hexo 中渲染 MathJax 数学公式 | 码迷
  5. Hexo 博客 MathJax 公式渲染问题 | 博客园
  6. Hexo 博客 MathJax 公式渲染问题 | 衡仔的技术小窝
  7. 如何处理 Hexo 和 MathJax 的兼容问题 | 林肯先生的 Blog
  8. 在 Hexo 中渲染 MathJax 数学公式 | 简书
  9. 在 Hexo 博客中使用 MathJax 写 LaTex 数学公式 | CSDN
  10. Hexo 中插入数学公式 | Steven’s Space
  11. 前端整合 MathjaxJS 配置笔记 | 博客园
  12. hexo-renderer-kramed | Github
  13. hexo-renderer-marked | Github
  14. MathJax.org
  15. The LaTex project