Hexo博客个性化配置

个性化的内容越来越多,About页面放不下了,于是决定单独写一篇文章记录本博客的配置

主题

NexT.Gemini

  • 版本:v8.29.0

不建议用npm安装,因为那样没法魔改。所以一如既往的直接clone就可以了。

背景图片(ribbon)

本来打算上传背景的,但是图片太大了,最后采用了动态生成的ribbon。

在博客的_config.yml,把canvas_ribbon的enable设置成true。

但是原版的ribbon.js有个小问题,就是如果在手机上点击,会连续刷新两次背景。看了一眼代码,问题出在这儿。

1
2
document.onclick = redraw;
document.ontouchstart = redraw;

因为不喜欢背景频繁变动,我把这两行给删了,同时也就不能用CDN提供的js文件了。

好在hexo允许自行配置vendors。在next的_config.yml里头找到vendors项目,然后加入下面内容(注意改成自己的js文件路径)

1
2
# 这里js文件被我放在 next\source\js\ribbon.min.js
canvas_ribbon: /js/ribbon.min.js

魔改footer

显示hexo和next版本号

页脚显示版本号这个特性在v7的next主题是有的,但是v8给去掉了。我个人比较喜欢这个特性,于是琢磨了半天nunjucks,把显示版本号给魔改回来了。

在footer.njk中把对应内容替换成下面这些~

1
2
3
4
5
6
7
8
9
10
11
12
13
// themes\next\layout\_partials\footer.njk

{%- if theme.footer.powered %}
<div class="powered-info">
<span class="post-meta-item">
{{- __('footer.powered', next_url('https://hexo.io', 'Hexo', {class: 'theme-link'}) + ' v' + hexo_version) }}
</span>
<span class="post-meta-item">
{%- set next_site = 'https://theme-next.js.org' if theme.scheme === 'Gemini' else 'https://theme-next.js.org/' + theme.scheme | lower + '/' %}
{{- __(' 主题 – ' + next_url(next_site, 'NexT.' + theme.scheme, {class: 'theme-link'}) + ' v' + next_version) }}
</span>
</div>
{%- endif %}

然后在locals.js替换下面内容

1
2
3
4
5
6
7
// themes\next\scripts\filters\locals.js

// 替换 const { config } = hexo;
const { version, config } = hexo;

// 添加下面这行
locals.hexo_version = version;

版权信息和字数统计并排

这个也是v7的next主题有的特性,升级到v8又给改没了。。。

此布局也已恢复在当前主题中;关闭版权信息时,字数统计仍会单独显示。

这个改起来就很简单了,无非就是把字数统计的span放到版权信息的div里头,如下所示。

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
<div class="copyright">
{%- set copyright_year = date(null, 'YYYY') %}
&copy; {% if theme.footer.since and theme.footer.since != copyright_year %}{{ theme.footer.since }} – {% endif %}
<span itemprop="copyrightYear">{{ copyright_year }}</span>
<span class="with-love">
<i class="{{ theme.footer.icon.name }}"></i>
</span>
<span class="author" itemprop="copyrightHolder">{{ theme.footer.copyright or author }}</span>


{%- if config.symbols_count_time.total_symbols or config.symbols_count_time.total_time %}
{%- if config.symbols_count_time.total_symbols %}
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="fa fa-chart-line"></i>
</span>
{%- if theme.symbols_count_time.item_text_total %}
<span>{{ __('symbols_count_time.count_total') + __('symbol.colon') }}</span>
{%- endif %}
<span title="{{ __('symbols_count_time.count_total') }}">{{ symbolsCountTotal(site) }}</span>
</span>
{%- endif %}

{%- if config.symbols_count_time.total_time %}
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="fa fa-coffee"></i>
</span>
{%- if theme.symbols_count_time.item_text_total %}
<span>{{ __('symbols_count_time.time_total') }} &asymp;</span>
{%- endif %}
<span title="{{ __('symbols_count_time.time_total') }}">{{ symbolsTimeTotal(site, config.symbols_count_time.awl, config.symbols_count_time.wpm, __('symbols_count_time.time_minutes')) }}</span>
</span>
{%- endif %}
{%- endif %}
</div>

页面载入动画速度

v8 不再使用 anime.js,动画序列改由 motion.js 中的 integrator 调度。文章列表默认使用 overlap: 100 错峰启动;把它改为 200(与默认 motion.duration 相同),就能让列表中的动画同时启动,减少逐项出现的等待感。当前主题已应用此修改。

1
2
3
4
5
6
7
// themes\next\source\js\motion.js

sequence.push({
targets,
complete: () => targets.classList.add('animated', animation),
overlap : 200
});

overlap 控制的是动画项的启动间隔,不是单项动画的播放时长。单项视觉动画的时长由 Animate.css 控制;如果要缩短淡入等效果本身,还需要调整 Animate.css 的动画时长。

字数统计和阅读时长

hexo-word-counter

  • 版本:v0.1.0
  • 代码块不计入字数(exclude_codeblock: true)

注意:安装完之后一定要执行hexo clean!

阅读进度条

next主题的_config.yml里找到Reading progress bar开启

首页隐藏分类(比如公告)

hexo-generator-index2

  • 版本:v0.2.0

分页页码省略

首页及归档、分类、标签页的分页由 themes/next/scripts/helpers/next-paginator.js 调用 Hexo paginator 生成。默认中间页码显示范围较窄,页数较多时会以省略号代替部分页码。

通过按总页数设置 show_all,可以让总页数不超过 5 页时显示全部页码;超过 5 页时继续使用原有省略方式:

1
show_all: this.page.total <= 5,

禁用更新日期

next主题的_config.yml > post_meta > updated_at > enable: false

全英语链接

全英语链接方便搜索引擎收录,这里简单修改hexo站点的_config.yml即可,下面给出本博客的配置

我的.md文件名都是英语,所以URL > permalink可以设置为:category/:title.html

文章的类别大多都是中文名,可以通过Category & Tag > category_map进行映射

1
2
3
4
5
6
# URL
:category/:title.html

# Category & Tag
category_map:
技术: tech

图片相关

图床(GitHub)

图床我直接使用的 GitHub,新建一个 repo,然后开启 GitHub Page(Settings > Pages)。Branch 选择 main,path 选择 root(根目录)。

然后就可以通过下面方式访问图片了。

1
2
# 假设图片放在 repo 的 img 文件夹里
![](https://{你的博客域名}/{图床repo名称}/img/{你的图片})

图片延迟加载(lazyload)

lazyload就是延迟加载,只有在看到图片的时候才加载,从而加快网页载入速度。

注:构建仍会显示 NexT 关于 lazyload 已弃用的警告,但当前实现有效

在 next 主题的 _config.yml 中启用 lazyload。当前实现会在 Markdown 图片转成 HTML 时处理文章和独立页面中的图片。

需要排除的图片可在 lazyload_exclude 中填写其 src 的一段文本;匹配是逐张图片进行的,不会因为某张图片命中就跳过整篇文章。比如 osu-sig 会让 About 页面上的 osu 统计图片保持原样。

其他图片会先显示加载占位图,再由 lozad 加载原图。下面是当前 themes/next/scripts/filters/post.js 的完整内容(替换该文件即可);占位图地址可在 placeholderUrl 中修改。

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
/* global hexo */

'use strict';

const { unescapeHTML } = require('hexo-util');

const placeholderUrl = 'https://oscarcx.com/hexo_resource/img/loading_cow.png';

function lazyloadImages(content, exclude) {
return content.replace(/<img\b[^>]*>/ig, image => {
if (/\sdata-src\s*=/i.test(image)) return image;

return image.replace(/\ssrc=(?:"([^"]*)"|'([^']*)'|([^\s>]+))/i, (attribute, doubleQuoted, singleQuoted, unquoted) => {
const src = doubleQuoted ?? singleQuoted ?? unquoted;
if (exclude && src.includes(exclude)) return attribute;
return ` src="${placeholderUrl}" data-src="${src}"`;
});
});
}

hexo.extend.filter.register('after_render:html', (html, locals) => {
const { lazyload, lazyload_exclude } = hexo.theme.config;
const content = locals.page?.content;
if (!lazyload || typeof content !== 'string' || !content) return html;
const images = content.match(/<img\b[^>]*>/ig) || [];
return images.reduce((result, image) => {
const lazyImage = lazyloadImages(image, lazyload_exclude);
return lazyImage === image ? result : result.replace(image, lazyImage);
}, html);
});

hexo.extend.filter.register('after_post_render', data => {
const { config } = hexo;
const theme = hexo.theme.config;
if (!theme.exturl && !theme.lazyload) return;
if (theme.lazyload) {
data.content = lazyloadImages(data.content, theme.lazyload_exclude);
}
if (theme.exturl) {
const siteHost = URL.parse(config.url)?.hostname || config.url;
// External URL icon
const exturlIcon = theme.exturl_icon ? '<i class="fa fa-external-link-alt"></i>' : '';
data.content = data.content.replace(/<a[^>]*\shref="([^"]+)"[^>]*>([^<]+)<\/a>/ig, (match, href, html) => {
// Exit if the href attribute doesn't exist.
if (!href) return match;

// Exit if the url has same host with `config.url`, which means it's an internal link.
const link = URL.parse(href);
if (!link?.protocol || link.hostname === siteHost) return match;

// Return encrypted URL with title.
const title = match.match(/title="([^"]+)"/);
const encoded = Buffer.from(unescapeHTML(href)).toString('base64');
if (title) return `<span class="exturl" data-url="${encoded}" title="${title[1]}">${html}${exturlIcon}</span>`;

return `<span class="exturl" data-url="${encoded}">${html}${exturlIcon}</span>`;
});
}
}, 0);

配置如下:

1
2
3
4
# Vanilla JavaScript plugin for lazyloading images.
# For more information: https://apoorv.pro/lozad.js/demo/
lazyload: true
lazyload_exclude: osu-sig

图片缩放(mediumzoom)

mediumzoom是一个点击放大图片的插件,挺实用的。

next主题的_config.yml,把mediumzoom设置成true

黑幕(spoiler)

文字模糊处理,点击可见(比如练习题答案)

插件项目地址:unnamed42/hexo-spoiler

安装:npm install hexo-spoiler --save(在博客根目录使用)

使用方式如下所示

1
2
{% spoiler text %}
{% spoiler option:value text... %}

渲染引擎(renderer)

把 hexo 自带的 hexo-renderer-marked 卸载了,现在使用的是 hexo-renderer-markdown-it-plus

配置如下所示:

1
2
3
markdown_it_plus:
typographer: false
quotes: “”‘’

typographer 这个功能是默认开启的,会在渲染过程中,自动把所有的英语引号全部换成中文引号。具体解释可以看 6.4.0版本之后的两个问题。

可以把下面两行复制到文章中进行测试,如果左右渲染出来一样,就是被自动替换了。

'(U+0027) ---> ’(U+2019)

"(U+0022) ---> “(U+201C) + ”(U+201D)

所以只需要 typographer: false 就能禁用这个坑爹的功能

公式显示

直接使用 katex,体积小,而且支持公式的自适应换行,安装方法见这篇官方教程:Math Equations | NexT。

hexo 自带的renderer 不支持 katex,所以我安装的是升级版 hexo-renderer-markdown-it-plus。

折叠块

本来想自己手搓一个的,发现有人已经写过,效果还不错,就不重复造轮子啦

插件:hexo-content-blocks

需要在所有页面共用的 <head> 布局中加载插件样式。本博客使用 NexT v8,对应文件为 themes/next/layout/_partials/head/head.njk。在主样式表引用后添加:

1
2
3
{%- if config.content_blocks.enable %}
<style type="text/css">{{ content_blocks_css() }}</style>
{%- endif %}

Hexo 根目录 _config.yml 中还需要启用插件并配置折叠块类型;本博客已有 content_blocks 配置。插件的完整选项见 hexo-content-blocks README。

效果如下:

我是折叠块

效果还不错吧~

其它改动

微调行距,字体大小等,并且在移动端也显示目录

全部统一放在 source\_data\styles.styl

然后把next主题的_config.yml的这部分取消注释就可以了。写在这个文件里的css会被添加到末尾,所以可以直接覆盖默认样式。

1
2
3
4
5
# Define custom file paths.
# Create your custom files in site directory `source/_data` and uncomment needed files below.
custom_file_path:
(省略若干)
style: source/_data/styles.styl