Hexo + NexT(或 Fluid)主题美化技巧

本文最后更新于:2026年8月30日 上午

1 网站界面

1.1 更改网站底部年份和版权信息之间的图标(NexT,Fluid)

在NexT 主题配置文件中找到Footer Settings修改配置项如下:

1
2
3
4
5
6
7
8
9
10
11
12
footer:
# Specify the year when the site was setup. If not defined, current year will be used.
#since: 2021

# Icon between year and copyright info.
icon:
# Icon name in Font Awesome. See: https://fontawesome.com/icons
name: fa fa-paw # 狗爪 paw
# If you want to animate the icon, set it to true.
animated: true # 动画显示
# Change the color of icon, using Hex Code.
color: "#000000" # 图标颜色

在 Fluid主题配置文件中查找Footer 修改相关配置。

1.2 更改网站图标(NexT,Fluid)

在\blog\source目录下创建资源管理的文件夹,例如\blog\source\uploads,将 4 份文件放入用户网站图标资源文件夹中\blog\source\uploads\user-favicons\1,并根据图片路径更改主题配置文件中的Favicon选项,更改内容如下:

1
2
3
4
5
6
favicon:
small: /uploads/user-favicons/1/favicon-16x16.png
medium: /uploads/user-favicons/1/favicon-32x32.png
apple_touch_icon: /uploads/user-favicons/1/apple-touch-icon.png
safari_pinned_tab: /uploads/user-favicons/1/logo.svg
#android_manifest: /manifest.json

创建网站图标可以在Favicon Generator网站上传 SVG 文件,可获得网站图标文件包,含有各种尺寸的 png 图片。

在 Fluid主题配置文件中查找Favicon修改图标路径如下,若无所示文件夹则在\source中创建:

1
2
3
# 用于浏览器标签的图标
# Icon for browser tab
favicon: /img/favicon.png

1.3 更改网站作者头像(NexT,Fluid)

在目录下创建资源管理的文件夹,例如\blog\source\uploads,将头像文件(常用图片格式)放入用户头像资源文件夹中\blog\source\uploads\user-avatars,并根据图片路径更改主题配置文件中的avatar选项,更改内容如下:

1
2
3
4
5
6
7
avatar:
# Replace the default image and set the url here.
url: /uploads/user-avatars/boy-blue.svg
# If true, the avatar will be displayed in circle.
rounded: true
# If true, the avatar will be rotated with the cursor.
rotated: false

在主题配置文件_config.fluid.yml 中查找 avatar 设置,位于About 页的头像,修改图片路径,若无所示文件夹则在\source中创建:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#---------------------------
# 关于页
# About Page
#---------------------------
about:
enable: true
banner_img: /img/default.png
banner_img_height: 60
banner_mask_alpha: 0.3
subtitle:
avatar: /img/avatar.png
name: "Leia"
intro: "技术博客,分享知识!"
# 更多图标可从 https://hexo.fluid-dev.com/docs/icon/ 查找,`class` 代表图标的 css class,添加 `qrcode` 后,图标不再是链接而是悬浮二维码
# More icons can be found from https://hexo.fluid-dev.com/docs/en/icon/ `class` is the css class of the icon. If adding `qrcode`, The icon is no longer a link, but a hovering QR code
icons:
- { class: "iconfont icon-github-fill", link: "https://github.com", tip: "GitHub" }
- { class: "iconfont icon-douban-fill", link: "https://douban.com", tip: "豆瓣" }
- { class: "iconfont icon-wechat-fill", qrcode: "/img/favicon.png" }

1.4 给文章添加访问密码(NexT)

在\source目录下的_data 文件夹(若无此文件夹则创建)中创建head.njk文件,在主题配置文件中查找custom_file_path选项,取消相应文件的注释。head.njk添加如下内容:

1
2
3
4
5
6
7
8
9
10
<script>
(function(){
if('{{ page.password }}'){
if(prompt('请输入文章密码') !== '{{ page.password }}'){
alert('密码错误!');
history.back();
}
}
})();
</script>

在创建的文章顶部的配置选项中添加 password 字段用于设置文章访问密码,例如:

image-20220219201529466-20220219201532

1.5 去除 NexT 主题下博客网站顶部的黑色宽线条(NexT)

在\blog\source目录下的_data 文件夹(若无此文件夹则创建)中创建styles.sty文件,在主题配置文件中查找custom_file_path选项,取消相应文件的注释。styles.sty添加如下内容:

1
2
//去掉顶部黑线:
.headband {display:none;}

1.6 添加站内搜索功能(NexT,Fluid)

安装hexo-generator-search

1
npm install hexo-generator-search --save

安装 hexo-generator-searchdb

1
npm install hexo-generator-searchdb --save

编辑主题配置文件,查找local_search设置enable为true,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
# Local Search
# Dependencies: https://github.com/next-theme/hexo-generator-searchdb
local_search:
enable: true
# If auto, trigger search by changing input.
# If manual, trigger search by pressing enter key or search button.
trigger: auto
# Show top n results per article, show all results by setting to -1
top_n_per_article: 1
# Unescape html strings to the readable one.
unescape: false
# Preload the search data when the page loads.
preload: false

Fluid 主题的站内搜索设置与上述步骤类似。

1.7 添加文章访问数统计,利用 LeanCloud 平台(Fluid)

注册LeanCloud账号,创建应用并进入设置,在存储选项中创建 Class,名称只能为 Counter,设置数据条目的默认为无限制,成功创建 Class。进入平台的设置项页面查看app_id和app_key的值,并将其填入主题配置文件中的 leancloud_visitors 设置项,并关闭 security,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
# Show number of visitors of each article.
# You can visit https://www.leancloud.cn to get AppID and AppKey.
leancloud_visitors:
enable: true
app_id: fgaUlM9tpzalN2J6sOooSVc0-gzGzoHszcz
app_key: LUzOsNy6KL0b2iyCMMlIstrJcz
# Required for apps from CN region
server_url: # <your server url>
# Dependencies: https://github.com/theme-next/hexo-leancloud-counter-security
# If you don't care about security in leancloud counter and just want to use it directly
# (without hexo-leancloud-counter-security plugin), set `security` to `false`.
security: false

在 LeanCloud 平台中设置 Web 安全域名,进入平台设置页面 - 应用安全中心,填入博客网站的地址。

为防止访问记录被随意篡改,进入云引擎 -> 部署 -> 在线编辑中,在弹出窗口选择 Hook 类型,在选择 Hook 处选择 beforeUpdate,在选择 Class 处选择 Counter 类。粘贴下方代码后,点保存。

1
2
3
4
5
6
7
8
var query = new AV.Query("Counter");
if (request.object.updatedKeys.indexOf('time') !== -1) {
return query.get(request.object.id).then(function (obj) {
if (obj.get("time") + 1 !== request.object.get("time")) {
throw new AV.Cloud.Error('Invalid update!');
}
})
}

然后单击创建按钮右侧的部署 -> 生产环境 -> 部署,待显示部署完成:1 个实例部署成功即可。在主题配置文件_config.fluid.yml 中查找网页访问统计,并设置 LeanCloud 的访问密钥:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 网页访问统计
# Analysis of website visitors
web_analytics: # 网页访问统计
enable: true
# LeanCloud 计数统计,可用于 PV UV 展示,如果 `web_analytics: enable` 没有开启,PV UV 展示只会查询不会增加
# LeanCloud count statistics, which can be used for PV UV display. If `web_analytics: enable` is false, PV UV display will only query and not increase
leancloud:
app_id: xxxxxxxxxxxxxx
app_key: xxxxxxxxxxxxxx
# REST API 服务器地址,国际版不填
# Only the Chinese mainland users need to set
server_url:
# 统计页面时获取路径的属性
# Get the attribute of the page path during statistics
path: window.location.pathname
# 开启后不统计本地路径 ( localhost 与 127.0.0.1 )
# If ture, ignore localhost & 127.0.0.1
ignore_local: true

1.8 更改网站背景图片(NexT)

在\source_data\style.styl(如若不存在则创建,并在主题配置文件中的 custom 设置项取消相应的注释),将背景图片 background.png 放入自定义文件夹\blog\source\uploads(若不存在则创建)中,并在 style.styl 文件中增加如下内容:

1
2
3
4
5
6
7
8
9
// background
body {
background-image:url(../uploads/background.png);
height:100%;
width:100%;
background-repeat:repeat-x;
background-attachment:fixed;
background-size:100% 100%;
}

2 功能配置

2.1 启用独立的主题配置文件,以便后续 NexT 主题的升级(NexT,Fluid)

在目录下新建_config.hexo-theme-next.yml,将\blog\themes\hexo-theme-next目录下的主题配置文件_config.yml中的内容全部复制到_config.hexo-theme-next.yml中,后续修改_config.hexo-theme-next.yml文件内容进行主题配置即可。

在目录下新建_config.fluid.yml,将\blog\themes\fluid目录下的主题配置文件_config.yml中的内容全部复制到_config.fluid.yml中,后续修改_config.fluid.yml文件内容进行主题配置即可。

3 警告错误解决

3.1 在部署完进行网站预览时,命令行窗口警告提示

1
2
3
4
5
6
(node:87224) Warning: Accessing non-existent property 'lineno' of module exports inside circular dependency (Use `node --trace-warnings ...` to show where the warning was created)accessing-non-existent-property-lineno-of-module-exports-inside-circu)
(node:87224) Warning: Accessing non-existent property 'column' of module exports inside circular dependency
(node:87224) Warning: Accessing non-existent property 'filename' of module exports inside circular dependency
(node:87224) Warning: Accessing non-existent property 'lineno' of module exports inside circular dependency
(node:87224) Warning: Accessing non-existent property 'column' of module exports inside circular dependency
(node:87224) Warning: Accessing non-existent property 'filename' of module exports inside circular dependency

Hexo 这里的 warning 是由于stylus导致的,幸运的是 stylus 在 0.54.8 版本修复了这个问题,所以对于 Hexo 用户来说,更新一下hexo-renderer-stylus,就可正常使用。如若仍报错,则定位到\blog\node_modules\nib目录中执行npm update stylus更新 stylus,解决过程中可能用到下列命令:

1
npm update hexo-renderer-stylus # 在\blog目录下执行更新hexo-renderer-stylus
1
2
3
npm update stylus # 在\blog\node_modules\nib目录下执行更新stylus
npm uninstall stylus # nib卸载stylus
npm install stylus # nib安装stylus

3.2 点击目录链接不跳转解决办法 (Hexo 版本:"6.0.0",hexo-theme-next 版本:"8.10.0")(NexT)

进入 _modules-toc.js 文件中,把 28 行~31 行修改为:

1
2
3
4
$title.attr('id', id);
// $title.children('a').remove();
// $title.html( '<span id="' + id + '">' + $title.html() + '' );
// $title.removeAttr('id');

3.3 Hexo 部署出现错误 err: Error: Spawn failed 解决方式

进入站点根目录,删除 git 提交内容文件夹

1
rm -rf .deploy_git/

3.4 Hexo 部署出现错误 Error [Nunjucks Error]: expected variable end:错误位置定位

Hexo 使用 Nunjucks 渲染转义内容。用{{ }}或{%`、`%}包装的内容将被解析,并可能导致问题。您可以使用原始标签插件将敏感内容包含起来。找到正文里面{{`、`}}、{%`、`%}这样的语句,主要是公式内容,作如下修改:

若是行内公式则将{{`、`}}用空格进行间隔开:{ {、} }

若是段外公式则在公式首尾加入如下内容,并且进入源码模式,检查确认段外公式最后留出一行空行(保证公式显示正确):

1
2
3
$${% raw %}
公式内容
$${% endraw %}

定位错误位置:

pPR68un

4 文章编辑

4.1 利用 typora 编辑 Hexo 博客

通过 typora 对发布在 Hexo 上的文章进行编辑,遇到需要加粗文字以及添加背景色时,使用如下代码:

1
<b> 加粗 - 背景色 - 文字 </b>

4.2 取消 NexT 主题侧边栏中目录的自动编号(NexT)

打开主题配置文件,查找 toc 选项,设置 number 选项为 false,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
# Table of Contents in the Sidebar
# Front-matter variable (nonsupport wrap expand_all).
toc:
enable: true
# Automatically add list number to toc.
number: false
# If true, all words will placed on next lines if header width longer then sidebar width.
wrap: false
# If true, all level of TOC in a post will be displayed, rather than the activated part of it.
expand_all: true
# Maximum heading depth of generated toc.
max_depth: 6

4.3 文章段落首行缩进(NexT)

首先进入电脑设置,找到键盘语言选项,打开半角/全角切换按键功能,切换至全角模式并在段落开头键入两个空格即为缩进两个字符,使用 tab 键并无效果,使用全角空格缩进效果如下:(这一问题貌似在更换 mathjax 渲染引擎时解决了???)

首行缩进两个字符;

首行缩进 3 个字符;

首行缩进 4 个字符;

Fluid 主题下使用&emsp;即可。

4.4 为文章在网站上显示添加 Read More 按钮(NexT)

在文章中任意位置插入HEXOMATH_1788059037004_31,在此之后的内容将被折叠隐藏。

4.5 数学公式显示(NexT,Fluid)

参考 NexT 主题官方教程Math Equations | NexT (theme-next.js.org),首先将站点配置文件中的 mathjax 项开启,如下所示

1
2
3
4
mathjax:
enable: true
# Available values: none | ams | all
tags: none

然后安装公式渲染引擎 hexo-renderer-pandoc,安装此插件前还需要安装pandoc,以及卸载掉其他渲染引擎,可能会用到如下命令:

1
npm uninstall hexo-renderer-marked
1
npm uninstall hexo-renderer-kramed
1
npm uninstall hexo-renderer-markdown-it

最后重启电脑,即可正确安装 pandoc 以及使用公式渲染引擎插件,效果如下:

行内公式测试

行间公式:

NexT 主题下文章内公式渲染使用教程链接:Math Equations | NexT (theme-next.js.org)

Fluid 主题下文章内公式渲染使用教程链接:配置指南 | Hexo Fluid 用户手册 (fluid-dev.com)

将复制 Mathtype 公式至 Markdown 中,首先将 Mathtype 预置中的剪切和复制预置设置为Latex2.0.9 and Later,并且注意公式中的{{`和`}}符号,之间一定要加上空格将其分开,否则公式显示不正确。

4.6 图片双 caption 渲染问题(Fluid)

图片会出现双标题显示的问题,在站点配置文件中加入

1
2
3
4
# 解决图片重复双标题显示的问题
pandoc:
extensions:
- '-implicit_figures'

4.7 MathJax 公式渲染编号显示问题

在 Hexo 中渲染 MathJax 数学公式 - 星空 str - 博客园 (cnblogs.com)

使用 \label{eq:1} 和 \tag{1} 给公式添加标签和编号,并使用 $\eqref{eq:1}$ 引用公式,实现跳转索引,如式 所示。

需要在 /Scripts 文件夹添加 js 脚本,自动将 \ref 转换成 \eqref

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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
"use strict";

/**
* Hexo 公式编号与引用兼容脚本
*
* Obsidian 原文:
*
* $$
* E = mc^2
* \tag{2.8}
* $$
*
* 由式 $\ref{eq:2.8}$ 可知。
*
* Hexo 构建时临时转换为:
*
* $$
* E = mc^2
* \tag{2.8}\label{eq:2.8}
* $$
*
* 由式 $\eqref{eq:2.8}$ 可知。
*
* 最终显示:
*
* 由式 (2.8) 可知。
*
* 注意:该脚本只修改构建时的内存内容,
* 不会修改 source/_posts 中的 Markdown 文件。
*/

const DEFAULTS = Object.freeze({
// 是否启用
enabled: true,

// Hexo filter 优先级,数值越小越早执行
priority: 5,

// 根据 \tag{2.8} 生成 \label{eq:2.8}
label_prefix: "eq:",

// parentheses:把 \ref 转换成 \eqref
// native:保持原来的 \ref
ref_style: "parentheses",

// true:只转换 eq: 开头的引用
// false:转换所有 \ref
only_equation_refs: true,

// 是否警告找不到目标的引用
warn_unresolved: true,

// 是否警告重复标签
warn_duplicate_labels: true,

// 是否输出转换统计
log_summary: true,

/**
* 旧引用映射。
*
* 例如公式实际是:
* \tag{2.35}
*
* 但正文仍然写:
* \ref{eq:2.8}
*
* 可配置:
* "eq:2.8": "eq:2.35"
*/
aliases: {},
});

const CONFIG = Object.assign(
{},
DEFAULTS,
hexo.config.equation_ref_compat || {}
);

CONFIG.label_prefix = String(
CONFIG.label_prefix || DEFAULTS.label_prefix
);

if (!CONFIG.aliases || typeof CONFIG.aliases !== "object") {
CONFIG.aliases = {};
}

const configuredPriority = Number(CONFIG.priority);

const FILTER_PRIORITY = Number.isFinite(configuredPriority)
? configuredPriority
: DEFAULTS.priority;

/**
* 保护代码块、行内代码和 HTML 注释。
*
* 避免修改示例代码中的:
* \tag
* \label
* \ref
* \eqref
*/
function protectLiteralSegments(source) {
const stored = [];

// 使用极不可能出现在文章中的占位符
const prefix = `\u0000HEXOMATH_${Date.now()}_`;

function save(segment) {
const token = `${prefix}${stored.length}\u0000`;
stored.push(segment);
return token;
}

let text = source;

/*
* Markdown fenced code block:
*
* ```language
* ...
* ```
*
* 或:
*
* ~~~language
* ...
* ~~~
*/
text = text.replace(
/(^|\n)[ \t]{0,3}(`{3,}|~{3,})[^\n]*\n[\s\S]*?\n[ \t]{0,3}\2[ \t]*(?=\n|$)/g,
save
);

// HTML 代码块
text = text.replace(
/<pre\b[^>]*>[\s\S]*?<\/pre>/gi,
save
);

text = text.replace(
/<code\b[^>]*>[\s\S]*?<\/code>/gi,
save
);

// HTML 注释
text = text.replace(
/<!--([\s\S]*?)-->/g,
save
);

// Markdown 行内代码
text = text.replace(
/(`+)([^\n]*?)\1/g,
save
);

return {
text,

restore(value) {
const tokenPattern = new RegExp(
`${prefix}(\\d+)\\u0000`,
"g"
);

return value.replace(
tokenPattern,
(_, index) => stored[Number(index)]
);
},
};
}

/**
* 将 tag 内容整理为可用于 label 的字符串。
*
* 示例:
* 2.8 → 2.8
* (2.8) → 2.8
* A.1 → A.1
* 2 - 8 → 2-8
*
* 不处理包含 LaTeX 命令或嵌套花括号的复杂 tag。
*/
function normaliseTag(rawTag) {
let value = rawTag.trim();

const outerParentheses = value.match(
/^\(([\s\S]*)\)$/
);

if (outerParentheses) {
value = outerParentheses[1].trim();
}

value = value.replace(/\s+/g, "");

if (!value || /[{}\\]/.test(value)) {
return null;
}

value = value
.replace(/[^A-Za-z0-9._:-]+/g, "-")
.replace(/^-+|-+$/g, "");

return value || null;
}

/**
* 根据 \tag 内容生成 label。
*
* \tag{2.8}
* ↓
* \label{eq:2.8}
*/
function labelFromTag(rawTag) {
const tag = normaliseTag(rawTag);

if (!tag) {
return null;
}

// 防止 tag 本身已经包含 eq:
return tag.startsWith(CONFIG.label_prefix)
? tag
: `${CONFIG.label_prefix}${tag}`;
}

/**
* 判断引用是否属于公式引用。
*/
function isEquationLabel(label) {
return (
!CONFIG.only_equation_refs ||
label.startsWith(CONFIG.label_prefix)
);
}

/**
* 处理单个块级公式内部内容。
*/
function processDisplayMath(body, stats) {
const labels = [
...body.matchAll(
/\\label\s*\{\s*([^{}]+?)\s*\}/g
),
];

/*
* 已有 label 时不再自动插入,
* 防止产生重复标签。
*/
if (labels.length > 0) {
return body;
}

const tags = [
...body.matchAll(
/\\tag\*?\s*\{\s*([^{}]+?)\s*\}/g
),
];

if (tags.length === 0) {
return body;
}

/*
* 一个公式块包含多个 tag 时,
* 无法安全判断每个标签应插入的位置。
*/
if (tags.length > 1) {
stats.skipped_multi_tag += 1;
return body;
}

const fullTag = tags[0][0];
const rawTag = tags[0][1];

const label = labelFromTag(rawTag);

if (!label) {
stats.invalid_tags.push(rawTag.trim());
return body;
}

stats.labels_added += 1;

return body.replace(
fullTag,
`${fullTag}\\label{${label}}`
);
}

/**
* 处理引用。
*
* 1. 应用旧标签映射;
* 2. 按配置把 \ref 转换为 \eqref。
*/
function transformReferences(text, stats) {
return text.replace(
/\\(eqref|ref)\s*\{\s*([^{}]+?)\s*\}/g,
(whole, command, rawLabel) => {
const originalLabel = rawLabel.trim();

const mappedLabel =
CONFIG.aliases[originalLabel] ||
originalLabel;

if (mappedLabel !== originalLabel) {
stats.aliases_applied += 1;
}

let targetCommand = command;

/*
* 只转换:
*
* \ref{eq:2.8}
*
* 不会把已有的:
*
* \eqref{eq:2.8}
*
* 再次修改。
*/
if (
CONFIG.ref_style === "parentheses" &&
command === "ref" &&
isEquationLabel(mappedLabel)
) {
targetCommand = "eqref";
stats.refs_converted += 1;
}

if (
targetCommand === command &&
mappedLabel === originalLabel
) {
return whole;
}

return `\\${targetCommand}{${mappedLabel}}`;
}
);
}

/**
* 检查文章中的标签和引用。
*/
function inspectReferences(text) {
const labelCounts = new Map();
const references = new Set();

for (
const match of text.matchAll(
/\\label\s*\{\s*([^{}]+?)\s*\}/g
)
) {
const label = match[1].trim();

labelCounts.set(
label,
(labelCounts.get(label) || 0) + 1
);
}

for (
const match of text.matchAll(
/\\(?:eqref|ref)\s*\{\s*([^{}]+?)\s*\}/g
)
) {
references.add(match[1].trim());
}

const duplicateLabels = [
...labelCounts.entries(),
]
.filter(([, count]) => count > 1)
.map(([label]) => label);

const unresolvedReferences = [
...references,
].filter(
label => !labelCounts.has(label)
);

return {
duplicateLabels,
unresolvedReferences,
};
}

/**
* 注册 Hexo 构建过滤器。
*/
hexo.extend.filter.register(
"before_post_render",

function equationReferenceCompatibility(data) {
if (
!data ||
!CONFIG.enabled ||
data.equation_ref_compat === false ||
typeof data.content !== "string" ||
data.content.length === 0
) {
return data;
}

const sourceName =
data.source ||
data.path ||
data.title ||
"unknown post";

const stats = {
labels_added: 0,
refs_converted: 0,
aliases_applied: 0,
skipped_multi_tag: 0,
invalid_tags: [],
};

/*
* 先保护代码等不应修改的内容。
*/
const protectedSource =
protectLiteralSegments(data.content);

let content = protectedSource.text;

/*
* 处理:
*
* $$
* ...
* $$
*/
content = content.replace(
/\$\$([\s\S]*?)\$\$/g,
(_, body) =>
`$$${processDisplayMath(body, stats)}$$`
);

/*
* 处理:
*
* \[
* ...
* \]
*/
content = content.replace(
/\\\[([\s\S]*?)\\\]/g,
(_, body) =>
`\\[${processDisplayMath(body, stats)}\\]`
);

/*
* 转换引用:
*
* \ref{eq:2.8}
* ↓
* \eqref{eq:2.8}
*/
content = transformReferences(
content,
stats
);

/*
* 检查转换后的引用状态。
*/
const inspection =
inspectReferences(content);

if (CONFIG.warn_duplicate_labels) {
for (
const label of inspection.duplicateLabels
) {
hexo.log.warn(
`[equation-ref] 重复标签 ${label}:` +
sourceName
);
}
}

if (CONFIG.warn_unresolved) {
for (
const label of
inspection.unresolvedReferences
) {
hexo.log.warn(
`[equation-ref] 找不到引用目标 ${label}:` +
sourceName
);
}
}

if (stats.skipped_multi_tag > 0) {
hexo.log.warn(
`[equation-ref] 跳过 ` +
`${stats.skipped_multi_tag} 个包含多个 ` +
`\\tag 的公式块:${sourceName}`
);
}

for (const tag of stats.invalid_tags) {
hexo.log.warn(
`[equation-ref] 无法由 \\tag{${tag}} ` +
`自动生成标签:${sourceName}`
);
}

if (
CONFIG.log_summary &&
(
stats.labels_added > 0 ||
stats.refs_converted > 0 ||
stats.aliases_applied > 0
)
) {
hexo.log.info(
`[equation-ref] ${sourceName}:` +
`补充标签 ${stats.labels_added},` +
`ref→eqref ${stats.refs_converted},` +
`别名替换 ${stats.aliases_applied}`
);
}

/*
* 恢复代码块和行内代码。
*/
data.content =
protectedSource.restore(content);

return data;
},

FILTER_PRIORITY
);

4.8 文章内容段落间距、文字间距设置

通过 hexo-fluid 的自定义 custom.css 进行修改设置,文件位于\themes\fluid\source\css若无此文件则新建一个,文件内容如下:

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
/*INFO 段落行间距设置*/
.post-content p {
letter-spacing:2px; /*文字间距为*/
line-height: 2.5; /*行间距为字体大小的 2.5 倍*/
margin-top: 0px; /*段前距离*/
margin-bottom: 0px; /*段后距离*/
text-align: justify; /*文本对齐方式*/
}

/*INFO 下划线设置*/
.post-content hr {
height: 1px; /*将高度设为 0*/
margin: 10px 0; /*设置间距*/
}

/*INFO 内容里列表*/
.post-content li {
margin-top: 15px;
margin-bottom: 15px;
}

/*INFO 内容里列表*/
.post-content ol {
margin-top: 0px;
margin-bottom: 0px;
}

然后在_config_fluid.yml文件中的custom_css项添加文件路径:

1
2
3
4
5
# 指定自定义 .css 文件路径,用法和 custom_js 相同
# The usage is the same as custom_js
custom_css:
- /css/indeximg-hover.css # 文章索引封面悬浮放大
- /css/custom.css # 文章段落

4.9 文章内图片并排显示

参考:Guide | Hexo Fluid Docs (fluid-dev.github.io)中的 Group Images 描述,为方便在 markdown 中预览图片,采用 html 方式预览,不影响渲染。

1
2
3
4
5
6
7
8
{% gi total n1-n2-... %}
<p align="center">
<img src="url" width="400" alt = "图片 1"/>
<img src="url" width="400" alt = "图片 2"/>
<img src="url" width="300" alt = "图片 3"/>
<img src="url" width="300" alt = "图片 4"/>
</p>
{% endgi %}
  • total:图片总数
  • n1-n2-... :每行中的图片数