升级到 Hexo 3 后站点文章渲染过慢的解决办法。

Hexo 3 自从放出更新到现在已经有很长一段时间了,相信很多人跟我一样都经历了一个非常曲折的升级过程。

第一个问题是我所编写的主题和插件全都阵亡了,这个问题在我决定升级 Hexo 之前已经被很多用户轰炸过,于是我花了两个晚上的时间对所有的主题和插件进行了大幅修改和升级。

另一个让我无比郁闷的问题是:官方虽然宣称 3.0 之后站点渲染速度更快了,但实际测试时我发现结果正好相反——全站 168 篇文章,在 Hexo 2.8.3 环境中渲染只需 3~4 秒时间,而更新到 Hexo 3.0 之后,网站的渲染居然花了一个多钟!

为了找出导致这个问题的 bottleneck ,我通过二分的手段很快定位出了问题文章 《ArchLinux安装配置OpenGL》,这篇文章乍一看没啥特别之处,但问题就出在文中这段长长的目录结构图:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
oglsuperbible5-read-only/ # OpenGL SuperBible 5th Edition 代码包
|
+-- freeglut-2.6.0/ # freeglut 安装包
|
+-- Linux/ # Linux Makefile 工程文件
| |
| +---- Chap01/
| | |
| | +---- Block/
| | |
| | +---- Makefile # Block 程序的Makefile
| |
| +---- ...
| |
|
+-- Src/ # 源代码
| |
| +---- Chap01/
| | |
| | +---- Block/
| | |
| | +---- Block.cpp # Block 程序源文件
| |
| +---- ...
| |
| |
| +---- GLTools/ # GLTools工具包
| | |
| | +---- include/ # 头文件
| | | |
| | | +---- GL/
| | | |
| | | +---- GLTools.h # GLTools头文件
| | | |
| | | +---- math3d.h # 矩阵类头文件
| | | |
| | | +---- ...
| | |
| | +---- src/ # 源文件
| | |
| | +---- GLTools.cpp # GLTools头文件
| | |
| | +---- math3d.cpp # 矩阵类源文件
| | |
| | +---- ...
| |
| +---- Models/
| |
| +---- OpenEXR/
|
+-- VisualStudio2008/ # VisualStudio 2008 工程文件
|
+-- VisualStudio2010/ # VisualStudio 2010 工程文件
|
+-- XCode/ # XCode 工程文件

对于这类语言无关的文本,我通常的做法是使用一对 ''' 包含,并且不带任何语言的说明。这种做法在 Hexo 2.x 中并没有问题,因为 Hexo 2 自带的语法高亮插件 highlight.js 在遇到没有语言说明的代码时是统一当成纯文本(plain)来解析的。而到了 Hexo 3 ,这样的内容就会使得 Hexo 3 的渲染变得奇慢无比。这是因为 Hexo 3 中的 highlight.js 会试图分析这段内容可能属于那种语言,内容越长,分析时间就越长。

解决办法就是在第一个 ''' 之后加上 plain 说明符,向 highlight.js 显式表明这是一段纯文本,那么当 highlight.js 解析到这段文本时,就会直接放弃语言类型的分析,从而大幅减少渲染时间。

考虑到我的博客中有很多这样的代码,我写了个脚本 speedup.py ,帮我在没有语言说明符的代码段后面加上 plain 说明符:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#!/usr/bin/python2
'''
SYNOPSIS:
$ python speedup.py -f FILE
or
$ python speedup.py -d DIR
'''
import sys, os, getopt
TARGET_TYPE = [".md", ".markdown"]
def process_file(path):
''' Process a file. '''
line = ""
quote_flag = False
line_list = []
with open(path) as f:
while True:
line = f.readline()
if line == "":
break
if line.startswith("```"):
quote_flag = not quote_flag
if line.strip()=="```" and quote_flag:
line = "\`\`\` plain\r\n"
line_list.append(line)
with open(path, 'w+') as f:
f.writelines(line_list)
def process_dir(path):
''' Process a directory. '''
file_list = []
files = os.listdir(path)
for file in files:
file = os.path.join(path, file)
root, ext = os.path.splitext(os.path.basename(file))
if os.path.isfile(file) and ext in TARGET_TYPE:
process_file(file)
def main():
if len(sys.argv) < 2:
print "Arguments should be at least 2."
print "python speedup.py -f [FILE]"
print "python speedup.py -d [DIRECTORY]"
exit(1)
try:
opts, args = getopt.getopt(sys.argv[1:], "f:d:", ["file=", "directory="])
for arg, value in opts:
if arg in ('-f', '--file'):
root, ext = os.path.basename(value)
if ext in 'TARGET_TYPE':
process_file(value)
elif arg in ('-d', '--directory'):
process_dir(value)
else:
print "Argument error. %s" % arg
exit(1)
except getopt.GetoptError as e:
print e
exit(1)
if __name__ == '__main__':
main()

使用方法很简单:

1
2
$ python speedup.py -f FILE # 优化单个 markdown 文件
$ python speedup.py -d DIR # 优化一个目录下的所有 markdown 文件