【配置】Hexo以及NexT8主题的配置

1 安装NexT v8.0

打开命令行,进入博客根目录,使用git下载NexT主题

1
2
# 下载NexT主题
git clone https://github.com/next-theme/hexo-theme-next themes/next

然后修改博客根目录下的_config.yml配置文件,将主题设置为NexT

1
theme: next

然后重新生成一下博客,打开本地服务

1
2
3
4
5
6
# 清除缓存
hexo clean
# 生成静态文件
hexo generate
# 启动服务(开启调试)
hexo server --debug

访问http://localhost:4000/,查看效果

2 配置修改

2.1 博客配置

以下配置,需添加至博客根目录下_config.yml

博客信息

1
2
3
4
5
title: 标题
subtitle: '子标题'
description: '描述'
keywords: 关键词
author: 作者

开启中文

1
language: zh-CN

修改文章永久链接

文章标题大多使用中文,但是URL并不推荐包含中文,也不利于SEO

1
2
url: https://gh1656409967.github.io/
permalink: posts/:hash/

自动检测高亮

1
2
3
highlight:
auto_detect: true
tab_replace: ' '

显示文章数

1
2
index_generator:
per_page: 30

每页页码

1
per_page: 30

默认分类

1
default_category: 测试

2.2 主题配置

以下配置,需添加至博客根目录下_config.next.yml

切换布局方案

NexT提供MuseMistPiscesGemini四种布局方案。

1
scheme: Pisces

添加子标题

1
index_with_subtitle: true

关闭页面底部博客信息

1
2
footer:
powered: false

添加自定义menu菜单

  1. 格式为Key: /link || icon,icon即为Font Awesome提供的图标。
  2. 修改主题目录下languages/zh-CN.yml,在menu下添加对应菜单的中文,例如gallery: 相册,注意统一缩进。
  3. 在博客根目录,运行 hexo new page "菜单",编辑新建的 source/菜单/index.md 即可。
1
2
3
menu:
about: /about/ || fa fa-user
gallery: /gallery/ || fa fa-camera

添加社交链接

1
2
3
4
5
social:
GitHub: https://github.com/GH1656409967 || fa-brands fa-github
QQ: http://wpa.qq.com/msgrd?v=3&uin=1656409967&site=qq&menu=yes || fa-brands fa-qq
网易云: https://music.163.com/#/user/home?id=270121274 || fa fa-music
豆瓣: https://www.douban.com/people/215985894/ || fa fa-video-camera

添加友链

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
links_settings:
icon: fa fa-link
title: 友情链接
layout: inline

links:
【jaihk662】: https://blog.csdn.net/jaihk662
【信仰.】: https://blog.csdn.net/haut_ykc
【SSimpLe_Y】: https://blog.csdn.net/ssimple_y
【柳婼のblog】: https://www.liuchuo.net/
【liweihang】: https://www.cnblogs.com/liweihang/
【ironz】: https://ironz.cn/
【Sysipus】: https://runzhaochen.github.io/
【Long_hen】: https://blog.csdn.net/Long_hen
【haut_bao】: https://blog.csdn.net/qq_41856950
【阿清そ 】: https://aqingya.cn/

目录设置

开启目录,关闭目录自动编号,开启目录自动换行,开启全部展开

1
2
3
4
5
toc:
enable: true
number: false
wrap: true
expand_all: true

开启文章结尾版权声明

1
2
creative_commons:
post: true

文章结尾打赏

添加文章结尾打赏图片,图片放至主题目录的source/images/

1
2
3
4
5
6
7
8
reward_settings:
enable: true
animation: false

reward:
wechatpay: /images/wechatpay.png
alipay: /images/thanks.png
bitcoin: /images/alipay.png

修改themes/next/languages/zh-CN.yml,文字与图片对应

1
2
3
4
reward:
wechatpay: 微信
alipay: 感谢
bitcoin: 支付宝

网站头像

图片放至主题目录的source/images/

1
2
avatar:
url: /images/avatar.png

网站图标

图片放至主题目录的source/images/

1
2
3
favicon:
small: /images/favicon-16x16-.png
medium: /images/favicon-32x32-.png

设置字体

设置字体为Fira Code和思源黑体

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
font:
enable: true

# Uri of fonts host, e.g. https://fonts.googleapis.com (Default).
host:

# Font options:
# `external: true` will load this font family from `host` above.
# `family: Times New Roman`. Without any quotes.
# `size: x.x`. Use `em` as unit. Default: 1 (16px)

# Global font settings used for all elements inside <body>.
global:
external: true
family: 'Fira Code,Noto Sans SC'
size:

# Font settings for site title (.site-title).
title:
external: true
family: 'Fira Code,Noto Sans SC'
size:

# Font settings for headlines (<h1> to <h6>).
headings:
external: true
family: 'Fira Code,Noto Sans SC'
size:

# Font settings for posts (.post-body).
posts:
external: true
family: 'Fira Code,Noto Sans SC'

# Font settings for <code> and code blocks.
codes:
external: true
family: 'Fira Code,Noto Sans SC'

设置代码高亮

1
2
3
codeblock:
theme:
light: atom-one-light

代码一键复制

1
2
3
4
codeblock:
copy_button:
enable: true
show_result: true

切换CDN

jsdelivr容易寄,可以换cdnjs或者unpkg

1
2
vendors:
plugins: unpkg

3 功能添加

3.1 图片不显示

此处仅处理本地图片引用,不考虑使用图床的情况

从之前生成的静态文件中,可以看到图片未上传至public目录,且图片路径是markdown源文件中的绝对路径,而不是相对路径。

首先修改博客根目录_config.yml配置文件,开启上传资源文件夹

1
post_asset_folder: true

然后需要安装一个图片路径转换插件,打开命令行,进入博客根目录,执行以下命令

1
npm i hexo-asset-image --save
图片路径转换插件

修改此插件源码/node_modules/hexo-asset-image/index.js,修改内容参看注释

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
...

if(config.post_asset_folder){
var link = data.permalink;
var beginPos = getPosition(link, '/', 3) + 1;
/* 注释以下内容
var appendLink = '';
// In hexo 3.1.1, the permalink of "about" page is like ".../about/index.html".
// if not with index.html endpos = link.lastIndexOf('.') + 1 support hexo-abbrlink
if(/.*\/index\.html$/.test(link)) {
// when permalink is end with index.html, for example 2019/02/20/xxtitle/index.html
// image in xxtitle/ will go to xxtitle/index/
appendLink = 'index/';
var endPos = link.lastIndexOf('/');
}
else {
var endPos = link.lastIndexOf('.');
}
link = link.substring(beginPos, endPos) + '/' + appendLink;
*/
/* 修改为下面两行 */
var endPos = link.lastIndexOf('/') + 1;
link = link.substring(beginPos, endPos);

var toprocess = ['excerpt', 'more', 'content'];

...

// 将所有'src'替换为'data-src'
$('img').each(function(){
if ($(this).attr('data-src')){
// For windows style path, we replace '\' to '/'.
var src = $(this).attr('data-src').replace('\\', '/');
if(!(/http[s]*.*|\/\/.*/.test(src)
|| /^\s+\//.test(src)
|| /^\s*\/uploads|images\//.test(src))) {
...

$(this).attr('data-src', config.root + link + src);
console.info&&console.info("update link as:-->"+config.root + link + src);
}
...

清除缓存,重新生成博客,打开本地服务,查看效果

3.2 MathJax

使用MathJax,开启LaTeX数学公式支持。首先更换解析器,打开命令行,进入博客根目录,执行以下命令

1
2
3
4
# 卸载hexo-renderer-marked
npm uninstall hexo-renderer-marked --save
# 安装hexo-renderer-pandoc
npm install hexo-renderer-pandoc --save
更换解析器

然后安装Pandoc,并且配置好环境变量。在博客根目录_config.next.yaml中添加

1
2
3
math:
mathjax:
enable: true

使用时需要在文章的front-matter里打开mathjax: true,简单测试

1
$$ x = {-b \pm \sqrt{b^2-4ac} \over 2a} $$

\[ x = {-b \pm \sqrt{b^2-4ac} \over 2a} \]

1
$$ f(a) = \frac{1}{2\pi i} \oint\frac{f(z)}{z-a}dz $$

\[ f(a) = \frac{1}{2\pi i} \oint\frac{f(z)}{z-a}dz \]

1
$$ \cos(\theta+\phi)=\cos(\theta)\cos(\phi)−\sin(\theta)\sin(\phi) $$

\[ \cos(\theta+\phi)=\cos(\theta)\cos(\phi)−\sin(\theta)\sin(\phi) \]

1
$$ \int_D ({\nabla\cdot} F)dV=\int_{\partial D} F\cdot ndS $$

\[ \int_D ({\nabla\cdot} F)dV=\int_{\partial D} F\cdot ndS \]

1
2
3
4
5
6
$$
\vec{\nabla} \times \vec{F} =
\left( \frac{\partial F_z}{\partial y} - \frac{\partial F_y}{\partial z} \right) \mathbf{i}
+ \left( \frac{\partial F_x}{\partial z} - \frac{\partial F_z}{\partial x} \right) \mathbf{j}
+ \left( \frac{\partial F_y}{\partial x} - \frac{\partial F_x}{\partial y} \right) \mathbf{k}
$$

\[ \vec{\nabla} \times \vec{F} = \left( \frac{\partial F_z}{\partial y} - \frac{\partial F_y}{\partial z} \right) \mathbf{i} + \left( \frac{\partial F_x}{\partial z} - \frac{\partial F_z}{\partial x} \right) \mathbf{j} + \left( \frac{\partial F_y}{\partial x} - \frac{\partial F_x}{\partial y} \right) \mathbf{k} \]

1
$$ \sigma = \sqrt{ \frac{1}{N} \sum_{i=1}^N (x_i -\mu)^2} $$

\[ \sigma = \sqrt{ \frac{1}{N} \sum_{i=1}^N (x_i -\mu)^2} \]

1
2
3
4
$$
(\nabla_X Y)^k = X^i (\nabla_i Y)^k
= X^i \left( \frac{\partial Y^k}{\partial x^i} + \Gamma_{im}^k Y^m \right)
$$

\[ (\nabla_X Y)^k = X^i (\nabla_i Y)^k = X^i \left( \frac{\partial Y^k}{\partial x^i} + \Gamma_{im}^k Y^m \right) \]

3.3 搜索功能

安装搜索插件

1
npm i hexo-generator-searchdb --save

打开 _config.next.yml ,添加

1
2
local_search:
enable: true

打开hexo的站点配置 _config.yml,添加

1
2
3
4
5
search:
path: search.json
field: post
format: html
limit: 10000

3.4 统计功能

显示文章字数统计、阅读时长及总字数。首先安装插件

1
npm i hexo-word-counter --save
Hexo统计插件

修改博客根目录_config.yml配置

1
2
3
4
5
6
7
8
9
symbols_count_time:
symbols: true
time: true
total_symbols: true
total_time: true
exclude_codeblock: true
awl: 2
wpm: 300
suffix: "分钟"

修改博客根目录_config.next.yml主题配置

1
2
3
symbols_count_time:
separated_meta: false
item_text_total: true

3.5 简易相册

得益于Markdown和HTML混编可以被部分解析器解析的优势,可以使用HTML+CSS,添加一个简易相册。

注意:之前由于为了使用MathJax而更换解析器为Pandoc,对于需要解析raw-html的文章,所有标签前不应有空格,且块级标签前后需要留出空行

首先hexo n page gallery,创建图库。修改图库页面gallery/index.md,按照以下格式添加相册,相册封面图片放至gallery/index/目录下。

1
2
3
4
5
6
7
8
9
10
11
12
<div class="gallery-list">
<div class="gallery-item">
<a href="相册1/"><img data-src="gallery/index/相册1封面.jpg"></a>
<p>-相册1-</p>
</div>

<div class="gallery-item">
<a href="相册2/"><img data-src="gallery/index/相册2封面.jpg"></a>
<p>-相册2-</p>
</div>
</div>

新建相册页面gallery/相册名/index.md,按照以下格式添加图片,图片放至gallery/相册名/index/目录下,其它相册同理。

1
2
3
4
5
6
7
8
9
10

<div class="img-list">
<div class="img-item"><img data-src="相册1/index/图片1.png" alt="图片1标题"></div>
<div class="img-item"><img data-src="相册1/index/图片2.jpg" alt="图片2标题"></div>
<div class="img-item"><img data-src="相册1/index/图片3.png" alt="图片3标题"></div>
<div class="img-item"><img data-src="相册1/index/图片4.png" alt="图片4标题"></div>
<div class="img-item"><img data-src="相册1/index/图片5.jpg" alt="图片5标题"></div>
<div class="img-item"><img data-src="相册1/index/图片6.png" alt="图片6标题"></div>
</div>

在博客根目录下 _config.next.yml 中添加

1
2
menu:
gallery: /gallery/ || fa fa-camera

在主题目录下,修改国际化文件languages\zh-CN.yml,添加

1
2
menu:
gallery: 相册

在博客根目录下,新建source/_data/styles.styl,添加自定义样式

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
// 自定义相册
.img-list, .gallery-list {
gap: 1rem;
grid-gap: 1rem;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
padding: 1rem;
align-items: center;
align-content: center;
justify-items: center;
justify-content: center;
}

.img-item img, .gallery-item img {
width: 250px;
height: 200px;
object-fit: cover;
border: 1px solid rgba(221, 221, 221, 0.5);
border-radius: 7px;
margin-top: 10px;
margin-left: 5px;
margin-right: 5px;
}

.gallery-item p {
margin: 0 auto;
max-width: 50%;
border: 1px solid $black-deep;
border-radius: 7px;
background: rgba(255, 255, 255, 0.3);
box-shadow: 0 8px 20px -8px rgba(0, 0, 0, 0.3);
color: $black-deep;
text-align: center;
font-size: 15px;
}

@media (max-width: 767px) {
.gallery-item p {
min-width: 75px;
font-size: 13px;
}
}

修改博客根目录下_config.next.yml,引用自定义样式

1
2
custom_file_path:
style: source/_data/styles.styl

3.6 添加分类和标签

打开命令行,进入博客根目录,执行以下命令

1
2
3
4
# 新建分类
hexo n page categories
# 新建标签
hexo n page tags

修改source/categories/index.md,在front-matter中添加

1
2
3
4
5
---
title: 分类
date: 2022-04-22 16:47:13
type: "categories"
---

修改source/tags/index.md,在front-matter中添加

1
2
3
4
5
---
title: 标签
date: 2022-04-22 16:47:30
type: "tags"
---

3.7 代码折叠

强推hexo博客代码折叠功能

在主题目录下,添加source/js/code-unfold.js

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
var CODE_MAX_HEIGHT = 250;
var containers = [];

// 展开
$('body').on('click', '.js_unfold_code_btn', function () {
$(this).closest('.js_highlight_container').addClass('on');
});
// 收起
$('body').on('click', '.js_retract_code_btn', function () {
var $container = $(this).closest('.js_highlight_container').removeClass('on');
var winTop = $(window).scrollTop();
var offsetTop = $container.offset().top;
$(this).css('top', 0);
if (winTop > offsetTop) {
// 设置滚动条位置
$('body, html').animate({
scrollTop: $container.offset().top - CODE_MAX_HEIGHT
}, 600);
}
});
// 滚动事件,触发动画效果
$(window).on('scroll', function () {
var scrollTop = $(window).scrollTop();
var temp = [];
for (let i = 0; i < containers.length; i++) {
var item = containers[i];
var {
$container,
height,
$hide,
hasHorizontalScrollbar
} = item;
if ($container.closest('body').length === 0) {
// 如果 $container 元素已经不在页面上, 则删除该元素
// 防止pjax页面跳转之后,元素未删除
continue;
}
temp.push(item);
if (!$container.hasClass('on')) {
continue;
}
var offsetTop = $container.offset().top;
var hideBtnHeight = $hide.outerHeight();
// 减去按钮高度,减去底部滚动条高度
var maxTop = parseInt(height - (hasHorizontalScrollbar ? 17 : 0) - hideBtnHeight);
let top = parseInt(
Math.min(
Math.max(scrollTop - offsetTop, 0), // 如果小于 0 ,则取 0
maxTop, // 如果大于 height ,则取 height
)
);
// 根据 sin 曲线设置"收起代码"位置
var halfHeight = parseInt($(window).height() / 2 * Math.sin((top / maxTop) * 90 * (2 * Math.PI / 360)));
$hide.css('top', Math.min(top + halfHeight, maxTop));
}
containers = temp;
});

// 添加隐藏容器
function addCodeWrap($node) {
var $container = $node.wrap('<div class="js_highlight_container highlight-container"><div class="highlight-wrap"></div></div>').closest('.js_highlight_container');

// 底部 "展开代码" 与 侧边栏 "收起代码"
var $btn = $(`
<div class="highlight-footer">
<a class="js_unfold_code_btn show-btn" href="javascript:;">展开代码<i class="fa fa-angle-down" aria-hidden="true"></i></a>
</div>
<a class="js_retract_code_btn hide-btn" href="javascript:;"><i class="fa fa-angle-up" aria-hidden="true"></i>收起代码</a>
`);

$container.append($btn);
return $container;
};

function codeUnfold() {
$('.highlight').each(function () {
// 防止重复渲染
if (this.__render__ === true) {
return true;
}
this.__render__ = true;
var $this = $(this);
var height = $(this).outerHeight();
if (height > CODE_MAX_HEIGHT) {
// 添加展开&收起容器
var $container = addCodeWrap($this, height);
containers.push({
$container,
height,
$hide: $container.find('.js_retract_code_btn'),
hasHorizontalScrollbar: this.scrollWidth > this.offsetWidth,
});
}
});
};

在NexT主题中全局引用jQuery,或者在_config.next.yml中开启Fancybox,Fancybox会依赖jQuery,

1
fancybox: true

引用code-unfold.js,在主题目录下,layout/_scripts/index.njk的最后添加

1
{{- next_js('code-unfold.js') }}

修改主题目录下source/js/next-boot.js

1
2
3
4
NexT.boot.refresh = function() {
// 添加一行代码
codeUnfold();
// ...

打开之前创建的source/_data/styles.styl,添加

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
// 展开收起效果
.highlight-container {
position: relative;
background-color: highlight-background;

&.on {
.highlight-footer {
display: none;
}

.hide-btn {
display: flex;
}

.highlight-wrap {
max-height: none;
}
}

.highlight-wrap {
overflow: hidden;
max-height: 200px;
}

.highlight-footer {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 60px;
background-image: 'linear-gradient(-180deg, rgba(255,255,255,0) 0%, %s 65%)' % highlight-background;
text-align: center;
}

.show-btn {
position: absolute;
bottom: 0;
left: 50%;
padding: 0 0.8em;
border-radius: 4px 4px 0;
color: #fff;
text-align: center;
text-decoration: none;
font-size: 12px;
line-height: 2em;
transform: translateX(-50%);

&:hover {
text-decoration: none;
}
}

.hide-btn {
position: absolute;
top: 0;
left: -21px;
display: none;
flex-direction: column;
padding: 0.1em 0 0.6em;
width: 22px;
border-radius: 4px 0 0 4px;
background-color: highlight-background;
color: #fff;
text-align: center;
text-decoration: none;
font-size: 12px;
line-height: 1em;
transition: top ease 0.35s;
}

.fa-angle-up, .fa-angle-down {
color: #fff;
font-style: normal;
}

.fa-angle-up:before {
content: '\f106';
}

.fa-angle-down:before {
margin-left: 0.5em;
content: '\f107';
}

.js_unfold_code_btn, .js_retract_code_btn {
border-bottom: none !important;
background: rgba(0, 0, 0, 0.5);

&:hover {
border-bottom-color: none !important;
}
}
}

3.8 折叠内容

推荐Hexo 静态博客添加可折叠内容

在主题目录下创建scripts/tags/fold_tag.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/* global hexo */
// Usage: {% fold Title %} Something {% endfold %}
function fold(args, content) {
var text = args[0];
if (!text) text = "点击显示/隐藏";
return '<div><div class="fold_hider"><div class="close hider_title">' +
hexo.render.renderSync({
text: text,
engine: 'markdown'
}).replace(/^<p>/, '').replace(/<\/p>$/, '') +
'</div></div><div class="fold">\n' +
hexo.render.renderSync({
text: content,
engine: 'markdown'
}) +
'\n</div></div>';
}
hexo.extend.tag.register('fold', fold, {
ends: true
});

在主题目录下创建source/js/fold_action.js

1
2
3
4
5
6
7
8
$(document).ready(function () {
$(document).on('click', '.fold_hider', function () {
$('>.fold', this.parentNode).slideToggle();
$('>:first', this).toggleClass('open');
});
//默认情况下折叠
$("div.fold").css("display", "none");
});

打开之前创建的source/_data/styles.styl,添加

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
// toggle hider title
.hider_title {
cursor: pointer;
background: #eeeeee;
}

// toggle indicators
.close:before {
padding-left: 0.5em;
padding-right: 0.5em;
content: '▼';
}

.open:before {
padding-left: 0.5em;
padding-right: 0.5em;
content: '▲';
}

// inline code
p code, .hider_title code {
color: #c71585;
background: #fffafa;
margin: 2px;
}

在博客根目录_config.next.yml中添加

1
2
custom_file_path:
bodyEnd: source/_data/body-end.njk

在博客根目录下创建source/_data/body-end.njk

1
2
{# 代码折叠 #}
<script type="text/javascript" src="/js/fold_action.js"></script>

使用fold标签,可以折叠内容

1
2
3
{% fold 折叠内容 %}
Hello World
{% endfold %}
折叠内容

Hello World

注意:如果开启了pjax,则需要修改主题目录下source/js/next-boot.js

1
2
3
4
NexT.boot.refresh = function() {
// 添加一行代码
$("div.fold").css("display", "none");
// ...

3.9 压缩

添加官方插件hexo-clean-csshexo-html-minifierhexo-uglify

1
npm i hexo-clean-css hexo-html-minifier hexo-uglify --save
Hexo压缩插件

在博客根目录_config.yml中添加

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
uglify:
mangle: true
# 这儿不能为空,为空会导致 js 文件压缩
# output:
# compress:
exclude:
- '*.min.js'
es6: false

clean_css:
exclude:
- '*.min.css'

html_minifier:
collapseBooleanAttributes: true
collapseWhitespace: true
# Ignore '<!-- more -->' https://hexo.io/docs/tag-plugins#Post-Excerpt
ignoreCustomComments: [ !!js/regexp /^\s*more/]
removeComments: true
removeEmptyAttributes: true
removeScriptTypeAttributes: true
removeStyleLinkTypeAttributes: true
minifyJS: true
minifyCSS: true

3.10 评论系统

Gitalk是一个基于GitHub Issue和Preact开发的评论插件。

首先需要去GitHub新建一个仓库,点击New创建

GitHub创建仓库

然后创建GitHub Application

GitHub创建应用

然后点击Generate a new client secret,验证GitHub密码后,保存好Client IDClient secrets

GitHub保存信息

在博客根目录下_config.next.yml中添加

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
comments:
style: buttons
active: gitalk
storage: true
lazyload: true
nav:
gitalk:
order: -2

gitalk:
enable: true
github_id: gh1656409967
repo: Gitalk
client_id: adfg89av4s9bf4s89nsn
client_secret: 1561aefeaegadadag5e1a68bbfsnyfsrg4587sr7
admin_user: gh1656409967
distraction_free_mode: true
proxy: https://cors-anywhere.azm.workers.dev/https://github.com/login/oauth/access_token
language: zh-CN

然后每次新生成文章/页面后,都需要去文章页初始化Issues

初始化Issues

点击使用GitHub登录后,再点击Authorize username

GitHub验证信息

刷新后即可开启评论

Gitalk评论

3.11 博文加密

首先打开命令行,进入博客根目录,执行以下命令,安装hexo-blog-encrypt插件

1
npm install hexo-blog-encrypt --save

在需要加密的文章开头front-matter中添加

1
2
3
4
password: 123456
abstract: 博文被加密了, 请输入密码查看。
message: 您好, 这里需要密码。
wrong_pass_message: 抱歉, 这个密码看着不太对, 请再试试。

注意,Fancybox可能会不加载,复制themes\next\source\js\third-party\fancybox.js的内容,添加到node_modules\hexo-blog-encrypt\lib\hbe.js

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
...

// 添加内容
function refreshfancybox() {
document.querySelectorAll('.post-body :not(a) > img, .post-body > img').forEach(element => {
const $image = $(element);
const imageLink = $image.attr('data-src') || $image.attr('src');
const $imageWrapLink = $image.wrap(`<a class="fancybox fancybox.image" href="${imageLink}" itemscope itemtype="http://schema.org/ImageObject" itemprop="url"></a>`).parent('a');
if ($image.is('.post-gallery img')) {
$imageWrapLink.attr('data-fancybox', 'gallery').attr('rel', 'gallery');
} else if ($image.is('.group-picture img')) {
$imageWrapLink.attr('data-fancybox', 'group').attr('rel', 'group');
} else {
$imageWrapLink.attr('data-fancybox', 'default').attr('rel', 'default');
}

const imageTitle = $image.attr('title') || $image.attr('alt');
if (imageTitle) {
// Do not append image-caption if pandoc has already created a figcaption
if (!$imageWrapLink.next('figcaption').length) {
$imageWrapLink.append(`<p class="image-caption">${imageTitle}</p>`);
}
// Make sure img title tag will show correctly in fancybox
$imageWrapLink.attr('title', imageTitle).attr('data-caption', imageTitle);
}
});

$.fancybox.defaults.hash = false;
$('.fancybox').fancybox({
loop: true,
helpers: {
overlay: {
locked: false
}
}
});
}

async function decrypt(decryptKey, iv, hmacKey) {
let typedArray = hexToArray(encryptedData);

const result = await cryptoObj.subtle.decrypt({
'name': 'AES-CBC',
'iv': iv,
}, decryptKey, typedArray.buffer).then(async (result) => {

...

// 添加内容
refreshfancybox();

return await verifyContent(hmacKey, decoded);
}).catch((e) => {
alert(wrongPassMessage);
console.log(e);
return false;
});

...

3.11 aplayer音乐播放器

首先打开命令行,进入博客根目录,执行以下命令,安装aplayer插件

1
npm install aplayer --save

source/_data/body-end.njk添加以下内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/aplayer/1.10.1/APlayer.min.css" integrity="sha512-CIYsJUa3pr1eoXlZFroEI0mq0UIMUqNouNinjpCkSWo3Bx5NRlQ0OuC6DtEB/bDqUWnzXc1gs2X/g52l36N5iw==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/aplayer/1.10.1/APlayer.min.js" integrity="sha512-RWosNnDNw8FxHibJqdFRySIswOUgYhFxnmYO3fp+BgCU7gfo4z0oS7mYFBvaa8qu+axY39BmQOrhW3Tp70XbaQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<div id="aplayer"></div>

<script>
const ap = new APlayer({
container: document.getElementById('aplayer'),
fixed: true,
listFolded: true,
lrcType: 3,
audio: [
{
name: 'AVICII UMF2016 Live',
artist: 'Avicii',
url: 'https://music.163.com/song/media/outer/url?id=440767926.mp3',
cover: 'http://p2.music.126.net/kmo__VMOex_fRScv2RXStA==/109951162842392590.jpg',
lrc: '/lyric/AVICII UMF2016 Live.lrc'
},
{
...
},
]
});
</script>

其中,APlayer.min.jsAPlayer.min.css可以使用CDN,也可以在GitHub上下载。配置内容的具体含义参考APlayer官方文档

url后面填写歌曲外链。打开网页版网易云音乐,选择非VIP歌曲,点开至歌词页面,将地址栏中的歌曲id,复制到https://music.163.com/song/media/outer/url?id=xxxx.mp3对应位置。

网易云外链

cover后面填写封面图片链接。以edge为例,按F12进入控制台,使用选择元素,选中封面,在对应的img标签内复制data-src属性的值。

网易云封面图片

lrc后面填写本地保存的歌词文件。以edge为例,按F12进入控制台,选择网络,搜索lyric,然后刷新歌词页面,找到对应的歌词文件,lrc对应外文歌词,tlyric对应翻译歌词,将lyric后面的值复制到的本地文件中,以.lrc格式保存,然后引用即可。我这里保存在主题目录下source/lyric/中。

网易云歌词

注意:链接跳转后播放会被打断,使用pjax可以解决这个问题。

3.12 豆瓣插件

注意:此方法适合像我这样的懒人,由于hexo升级至6.1.0后,不再使用ejs等,建议能折腾的好好重写这个插件。

首先打开命令行,进入博客根目录,执行以下命令,安装hexo-douban豆瓣插件

1
npm install hexo-douban --save

在博客根目录_config.yml中添加

1
2
3
4
5
6
7
8
9
10
douban:
user: lordash
builtin: false
book:
title: 'This is my book title'
quote: 'This is my book quote'
movie:
title: 'This is my movie title'
quote: 'This is my movie quote'
timeout: 10000

user豆瓣用户名,登录豆瓣,点击个人主页,地址栏URL中https://www.douban.com/people/xxxxxx/后的xxxxxx即是个人ID。title页面标题,builtin是否将生成页面功能嵌入hexo g中,默认为false,quote是页面开头的一段话,timeout是豆瓣接口超时时间,默认为10000毫秒,如果在使用时发现报了超时的错(ETIMEOUT)可以把这个数据设置的大一点。

然后修改_config.next.yml,添加

1
2
3
menu:
books: /books/ || fa fa-book
movies: /movies/ || fa fa-film

修改themes\next\languages\zh-CN.yml,添加

1
2
3
menu:
books: 书籍
movies: 影音

使用hexo douban生成豆瓣页面,命令行提示

1
2
INFO  0 books have been loaded in 858 ms, because you are offline or your network is bad
INFO 0 movies have been loaded in 1751 ms, because you are offline or your network is bad

根据打印,在node_modules\hexo-douban\lib\books-generator.js中添加打印

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
...
var offline = false;
# 添加hexo-log引用
var log = require('hexo-log')({
debug: false,
silent: false
});

var log = require('hexo-log')({
debug: false,
silent: false
});

function resolv(url, timeout, headers) {

var response = '';
try {
response = request(url, {
timeout: timeout,
dataType: 'xml',
headers: headers,
});
} catch (err) {
# 打印详细错误日志
log.error(err);
offline = true;
}
...

再次hexo douban生成,可以看到详细错误提示,

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
ERROR E:\work\blog\node_modules\urllib-sync\request.js:44
var filepath = path.join(os.tmpDir(), name);
^

TypeError: os.tmpDir is not a function
at E:\work\blog\node_modules\urllib-sync\request.js:44:31
at done (E:\work\blog\node_modules\urllib\lib\urllib.js:396:5)
at E:\work\blog\node_modules\urllib\lib\urllib.js:628:9
at decodeContent (E:\work\blog\node_modules\urllib\lib\urllib.js:469:14)
at IncomingMessage.<anonymous> (E:\work\blog\node_modules\urllib\lib\urllib.js:593:7)
at IncomingMessage.emit (node:events:538:35)
at endReadableNT (node:internal/streams/readable:1345:12)
at processTicksAndRejections (node:internal/process/task_queues:83:21)

Error: E:\work\blog\node_modules\urllib-sync\request.js:44
var filepath = path.join(os.tmpDir(), name);
^

TypeError: os.tmpDir is not a function
at E:\work\blog\node_modules\urllib-sync\request.js:44:31
at done (E:\work\blog\node_modules\urllib\lib\urllib.js:396:5)
at E:\work\blog\node_modules\urllib\lib\urllib.js:628:9
at decodeContent (E:\work\blog\node_modules\urllib\lib\urllib.js:469:14)
at IncomingMessage.<anonymous> (E:\work\blog\node_modules\urllib\lib\urllib.js:593:7)
at IncomingMessage.emit (node:events:538:35)
at endReadableNT (node:internal/streams/readable:1345:12)
at processTicksAndRejections (node:internal/process/task_queues:83:21)

at request (E:\work\blog\node_modules\urllib-sync\index.js:44:13)
at resolv (E:\work\blog\node_modules\hexo-douban\lib\books-generator.js:25:20)
at Hexo.module.exports (E:\work\blog\node_modules\hexo-douban\lib\books-generator.js:134:23)
at Hexo.tryCatcher (E:\work\blog\node_modules\bluebird\js\release\util.js:16:23)
at Hexo.<anonymous> (E:\work\blog\node_modules\bluebird\js\release\method.js:15:34)
at E:\work\blog\node_modules\hexo\lib\hexo\index.js:407:22
at tryCatcher (E:\work\blog\node_modules\bluebird\js\release\util.js:16:23)
at MappingPromiseArray._promiseFulfilled (E:\work\blog\node_modules\bluebird\js\release\map.js:68:38)
at MappingPromiseArray.PromiseArray._iterate (E:\work\blog\node_modules\bluebird\js\release\promise_array.js:115:31)
at MappingPromiseArray.init (E:\work\blog\node_modules\bluebird\js\release\promise_array.js:79:10)
at MappingPromiseArray._asyncInit (E:\work\blog\node_modules\bluebird\js\release\map.js:37:10)
at _drainQueueStep (E:\work\blog\node_modules\bluebird\js\release\async.js:97:12)
at _drainQueue (E:\work\blog\node_modules\bluebird\js\release\async.js:86:9)
at Async._drainQueues (E:\work\blog\node_modules\bluebird\js\release\async.js:102:5)
at Immediate.Async.drainQueues [as _onImmediate] (E:\work\blog\node_modules\bluebird\js\release\async.js:15:14)
at processImmediate (node:internal/timers:466:21)

看来是由于Node.js升级后,os.tmpDir()方法失效导致。我们使用os.tmpdir()替换node_modules\urllib-sync\request.js中的os.tmpDir(),再次使用hexo douban后,生成成功。

使用pjax,还需要修改node_modules\hexo-douban\lib\templates\book.ejs

1
2
3
4
5
/* 添加data-pjax属性 */
<script data-pjax>
<% include index.js %>
<% include pagination.js %>
</script>