这个页面记述了我在使用Hexo框架搭建网站时的流程和一些问题的解决方法。
不过是一些技术方面的备忘我不是程序员,也不懂这方面的东西,所以我会写得很通俗,表达有些不准确的地方,欢迎留言指正。

建立Hexo站点

准备

Hexo的建立需要系统具备可以运行它的环境,这点看文档就行了。

文档地址:https://hexo.io/zh-cn/docs/

建站

文档记述得比较清楚,理解难度不大,直接照做就是了。

部署

在参照文档完成主题(Theme)基本配置后(更详细的主题配置以后再说),此时一个完整的站点已经在本地搭建好了,现在就是要把它放到网络上。这个过程叫部署,文档提供了多种部署方式,我直接使用了Git的“一键部署”,省事。部署事前的准备可以看文档。

由于我使用的是Github Page,所以在开始部署前,应当额外建立一个名为Hexo的分支(Branch),此前的Master分支(部署后分支名称会变成Main)存储着框架代码,而新分支则负责站点的代码,并在此提交写作。建立分支可以在之后避免不少麻烦。

之后是通过ssh Key让我的Github仓库与本地站点建立联系,并便于后续更新。步骤如下:

SSH key配置

1.检查本机是否有SSH key设置

如果是使用普通安装的方式安装了Git,在资源管理器的右键菜单会有Git Bash Here选项。单击它会有新窗口,之后输入命令行都是在这个窗口中进行,不再多说。

在这个窗口输入:$ cd ~/.sshcd .ssh

如果没有则提示: No such file or directory

如果有,可以去SSH key的本地存储文件夹看看,位于用户文件夹下的.ssh子文件夹中。如果不知道这个SSH key,就删除整个文件夹,重新创建一个。如果知道的话可以直接跳过2.3.4.步骤直接测试SSH key。

2.创建密钥,生成SSH key

Git Bash输入ssh-keygen -t rsa -C "输入你的邮箱"创建密钥。

当生成ssh key时,

Enter file in which to save the key (/c/Users/diyvc/.ssh/id_rsa):   #不填直接回车
Enter passphrase (empty for no passphrase):   #输入密码(可以为空)
Enter same passphrase again:   #再次确认密码(可以为空)

成功截图

生成如上图所示标识生成成功了,存放在用户文件夹下。

文件夹截图

3.将SSH key添加到Github中

登录GitHub,在账号设置的Settings→SSH and GPG keys→New SSH key,输入一个title和Key。Key在.sshid_rsa.pub里面,打开它,将密钥复制进去就可以了。

4.配置账户

Git Bash输入

$ git config --global user.name “your_username”  #设置用户名
$ git config --global user.email “your_registered_github_Email”  #设置邮箱地址(用注册Github的邮箱)

5.测试SSH key是否设置成功

Git Bash输入$ ssh -T git@github.com,一般会出现

1
2
3
4
5
The authenticity of host 'github.com (192.30.253.112)' can't be established.
RSA key fingerprint is SHA256:nThbg6*******************ARLviKw6E5SY8.
Are you sure you want to continue connecting (yes/no)? yes #确认你是否继续连接,输入yes
Warning: Permanently added 'github.com,192.30.253.112' (RSA) to the list of known hosts.
Enter passphrase for key '/c/Users/xxxx_000/.ssh/id_rsa':  #生成ssh kye是密码为空则无此项,若设置有密码则有此项且,输入生成ssh key时设置的密码即可。

6.将本地项目通过SSH提交到GitHub上

打开Github存储着Github Pages的仓库,进入配置页: Settings – SSH and GPG keys,添加本地生成的ssh秘钥,选择New SSH key,复制id_rsa.pub的密钥到Key中。

再次测试一下,在存放着本地站点的文件夹中Git Bash,输入ssh -T git@github.com ,出现类似Successfully或者类似字样,代表成功了,这样就可以将本地站点文件推送到远方的Github仓库中。

现在在本地站点文件夹打开Git Bash,输入hexo deploy,将本地站点推送到Github仓库。这样就部署完成了。

开始写作

在本地站点文件夹Git Bash,执行下列命令来创建一篇新文章或者新的页面,一般情况下新建的页面都是可以被渲染为HTML的Markdown文档。

1
$ hexo new [layout] <title>

layout 是可选参数,用以指定文章类型,若无指定则默认由配置文件中的 default_layout 选项决定。

title 是必填参数,用以指定文章标题,如果参数值中含有空格,则需要使用双引号包围。

scafford\文件夹下配置了 post、page、draft 三种 Layout 模板。默认为 post,可以通过修改站点配置文件 _config.yml 中的 default_layout 参数来指定默认布局。如果不需要模板,也可以设置 layout: false ,这样新建的页面就不会受到主题的渲染。

参见文档: Writing

如果选择使用Markdown渲染文章,可以参见Markdown语法手册

更新文件

本地调试(启动本地服务器)

本地调试可以使用hexo server命令,以启动本地服务器,一般可以简写为hexo s

1
$ hexo server

参见文档: Server

清理缓存文件

1
$ hexo clean

还可简写为hexo cl

生成静态文件

生成静态文件即生成更新后的本地网站文件以用于推送至网络,在生成时会比对文件的 SHA1 checksum,只有变动的文件才会写入。使用以下命令:

1
$ hexo generate

还可简写为hexo g

参见文档: Generating

部署更新

1
$ hexo deploy

还可简写为hexo d[1]

参见文档: Deployment

主题(Theme)

Hexo默认的Landscape可自定义的内容主题比较简陋,如果想要自己的站点更好看,Hexo的主题页面收录丰富的主题供选择。

其中Next主题是其中最受欢迎的,站点在最初写下这篇文章时使用的正是这个主题。不过NExT主题的局限性随着使用的人日趋增多而愈发明显,一是可通过主题配置文件简单自定义的内容较少,大多数美化都需要自行编写代码,二是无论怎么美化都不会脱离最初的主题框架,这又必然导致站点界面的同质化。因此之后我又尝试了几种主题。

安装和调试Next主题,可以直接参考文档。特别提醒,如果你和我一样对Git完全不熟的话,还是选择“下载稳定版本”为好,当初“克隆最新版本”给我带来了不少麻烦,比如无法推送主题文件到仓库,而且这个问题至今没有解决。

根据一般大主题的文档附带的注释,加上一点英文理解,就可以直接改站点和主题的YAML配置文件。主题不同,可配置的内容会有差异,某种程度上可以说一个主题的特色就在于主题配置文档中。都用主题了肯定是不愿意花太多时间写代码的啦对于主题配置文件未提到的修改,可以通过在\source\_data下创建对应的文件,实现不修改源代码的情况下自定义主题的样式,不过如果还要进一步魔改,只有改源码了。

配置页面头像、背景

这事只需要参照配置文件做就行了,但有一点需要提醒,如果选用本地文件,路径最好填写生成页面(hexo gernerate)后的图片路径

其他与Hexo相关的命令

从远程仓库拉取(同步)

适用于在其他地方更新了远程仓库比如说移动端编辑,那么此时所在本地的仓库版本是低于远程仓库的,这时就要用到它。

git pull origin source

本地内容提交到远程仓库

另外一种方法。

git add .
git commit -m "提交说明"
git push -u origin source

更换预览端口

hexo server -p 6000

排除错误

在出现错误的命令后添加--debug

另:移动端编辑方案

需要配置Github Actions,配置方法之后有。

  1. 在Github源码仓库/分支直接编辑。
  2. 使用HexoPlusPlus插件,还需要将手中的一个域名托管在Cloudflare(也可以不托管就是比较麻烦罢了)。

进阶配置

但无论何种主题,有些共同的工作都是要做的,接下来将记录这些工作。

新建页面

一个Hexo构建的页面最初只有首页和归档,对于一个站点而言这多半是不够的,因此需要新建 分类、标签、关于、友链、404首先要在主题中开启这些页面的菜单入口,一般是在主题配置文件下的 menu 一栏修改,大多数主题的格式都是:

	菜单名: /菜单目录 || 菜单图标名字

有些主题还会支持二级菜单,如Volantis主题:

1
2
3
4
5
分类: /categories/ || fas fa-folder-open
更多||fas fa-ellipsis-v:
影视: /movies/ || fas fa-video
图书: /books/ || fas fa-book-open
游戏: /games/ || fas fa-gamepad

建立入口之后,需要创建这个页面,在根目录下Git Bash

1
hexo new page xxx

xxx为要创建的页面,这样就会在\source文件夹下创建对应页面的文件夹,里面有一个 index.md 文件,可以根据自己的需要在这个 Markdown 文件中写一些内容,就像写文章一样。

404页面一般需要设置layout: false避免主题渲染成类似文章一样的页面,HTML代码可自行设定。

RSS订阅

安装插件:

1
npm install hexo-generator-feed --save

在站点配置文件_config.yml中添加以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
# RSS
feed:
type: rss2 # RSS的类型(atom/rss2),设定为 ['atom', 'rss2'] 则两者都启用
path: rss2.xml # 文件路径
limit: 20 # 展示文章的数量,使用 0 或 false 代表展示全部
hub: # PubSubHubbub URL,不需要则留空
content: # 可选,在RSS文件中是否包含整个页面的内容,默认不填为false
content_limit: 140 # 可选,自动截取的摘要长度
content_limit_delim: ' ' # 可选,手动截取摘要的分隔符,启用后RSS文件只会显示页面的摘要
order_by: -date # 文章顺序
icon: icon.png # feed 图标
autodiscovery: true # Add feed autodiscovery.
template: # XML模板路径。这个文件将被用来生成 xml 文件。自带两个默认模板:atom.xml 和 rss2.xml。

使用谷歌提供的PubSubHubbub 工具,查看是否成功传输到读者端。填入 Feed URL,然后 Get Info。

为Github Pages配置Websub

WebSub 前身为 PubSubHubbub,原本由 Google 开发,现为 W3C 下的开源协议。WebSub 基于 webhook ,用于发布者与订阅者之间的通信。hub 中转验证订阅者发来的订阅请求。当有更新,hub 将发布者的新内容分发到订阅者手中。
每当你更新一篇文章,就会通知「中转」,支持 WebSub 的 Feed 解析器收到有新内容的通知。相比之前不断地下载 Feed,使用 WebSub 后,将大大减少下载次数,做到「有更才下」。

feed配置的设置 hub: https://pubsubhubbub.appspot.com

在 GitHub Pages 仓库下,Settings -> Webhooks -> Add webhook,Add Webhook。

Payload URL填写为:

	Pubsubhubbub URL?hub.mode=publish&hub.url=你的站点 Feed URL

https://pubsubhubbub.appspot.com/publish?hub.mode=publish&hub.url=https://blog.haysc.tech/example_feed.xml

Content typeapplication/x-www-form-urlencoded

在 Which events would you like to trigger this webhook? 问题下,选Let me select individual events.,然后仅选Page builds

手动激活 Webhook 进行测试,在 Recent Deliveries 下查看 Response 结果,得到结果 204 意味测试成功。

选择评论系统

Hexo支持的评论系统很多,可参见各个主题和评论的配置文档。

本站选择了Gitalk和Waline两个评论系统,Gitalk配置很简单,这里不细说,说说Waline吧。

我把Waline的服务端放在了Vercel,因此需要更改环境变量,但每次环境变量配置完后,根据文档需要重新部署,但怎么重新部署文档没说清楚,仓库的Issues也没人提出这个问题(或许是这个步骤太简单了吧)。一开始我以为是Github仓库重新部署,但部署后发现不生效,后来一想服务端更新了怎么跑去仓库部署呢。但Vercel怎么重新部署,网站文档也没讲清楚。

一次偶然机会,我才发现了重新部署在Vercel项目下的Deployments菜单,选择最近一次Deployments进入,在Visit按钮旁边的选单中有一个Redeploy,就是它了。

图床

开启本地图床

安装hexo-asset-image插件

1
npm install hexo-asset-image --save

在站点配置文件_config.yml中,设置post_asset_folder: true

之后每当生成新页面时,hexo会自动在/source/_posts目录下创建同名的文件夹,只需在Markdown文档里使用![title](图片名.jpg),无需路径名就可以插入图片。

Github图床+PicGo上传+jsDelivr加速

注意:此方法是对Github服务的滥用,违反注册账号时的有关协议。Github本身可视作提供外链的文件存储库,自然可以用来做图床。而且利用 jsDelivr CDN 加速访问(jsDelivr 是一个免费开源的 CDN 解决方案),PicGo 工具一键上传,操作简单高效,GitHub 和 jsDelivr 都是知名网站,不用担心其他图床存在的跑路问题,也不用担心速度和容量问题,而且完全免费。

PicGo 还有相册功能,可以对已上传的图片进行删除,修改链接等快捷操作,还可以生成不同格式的链接、支持批量上传、快捷键上传、自定义链接格式、上传前重命名等,功能全面。设置步骤如下:

  1. 新建一个仓库,填写好仓库名,仓库描述。
  2. 在主页依次选择【Settings】-【Developer settings】-【Personal access tokens】-【Generate new token】,填写好描述,勾选【repo】,然后点击【Generate token】生成一个Token,注意这个Token只会显示一次。
  3. 下载PicGo,安装好后开始配置图床。
  4. 按照工具要求进行配置,在自定义域名下设置 https://cdn.jsdelivr.net/gh/用户名/图床仓库名 ,这样上传完毕后,我们就可以通过 https://cdn.jsdelivr.net/gh/用户名/图床仓库名/图片路径 加速访问图片。[2]

Gitee图床配置同理,只不过不需要CDN加速,但PicGo原生不支持配置Gitee仓库,需要在的插件中下载Gitee插件。此外Gitee免费版仓库有5G容量限制,超过1M的图片无法展示,只能登录后查看。本站为两个图床根据实际情况并用。

图床网站

推荐 牛图网、imgbbsm.ms 图床(需要注册账号)、catbox.moe 图床(它还可以存储音频,如今中国访问已不稳定,适合境外存储)、Imgur(图片社区,可以传图,但中国大陆地区无法访问)。

Onedrive图床+本地程序上传

配合Onemanager实现,优点是配置不算复杂,容量很大(1T到5T不等),可自定义域名,缺点是有随时被微软收回的风险,以及无法在新标签页中预览图片(打开新标签页会直接下载)。

阿里云OSS+PicGo上传

本来没有打算使用付费的云服务的,可阿里云对象存储服务(OSS)价格实在太便宜了,1年8元。而且阿里云服务稳定,既不用担心 sm.ms 没有自定义域名,害怕上传的图片找不到,也没有Github和Onedrive的滥用或随时可能到期的顾虑,更不用考虑阿里云的突然蒸发问题。使用阿里云还有另一个优势,便是可以自动压缩和转换图片格式为.webp,以及防盗链和自定义域名。甚至可以托管像本站这样的静态网站

如果域名没有ICP备案的话,Bucket地域建议选择中国(香港)。防盗链配置的话需要添加协议,还有本地预览的地址也要加上(通配符有没有用没有试)。修改读写权限为“读写权限”,配合PicGo软件同样可以实现自动上传,简单高效。

当然也有缺点,手机上传图比较麻烦,打开阿里云的控制台各种验证也很麻烦。

文章永久链接

Hexo在生成博客文章链接时,默认是按照年、月、日、标题格式来生成,这样对中文在生成链接时会被转码,导致链接过长,对访客不友好,同时一旦更改文章标题,原URL就会失效,不易保存分享。利用hexo-abbrlink插件可以为每篇文章自动生成或手动指定一个abbrlink,这样除非删除文章,就能保证生成的链接能永久生效。

安装hexo-abbrlink2插件

1
npm install hexo-abbrlink2 --save

设定站点配置文件中的文章生成链接permalink

1
permalink: posts/:abbrlink/

自动生成是在hexo generate命令时会为每篇第一次生成的文章自动指定一个永久id(不是在创建新文章时)。自动生成有两种方式:

第一种是依靠自动算法,在站点配置文件permalink下一行:

1
2
3
4
permalink: posts/:abbrlink/
abbrlink:
alg: crc32 #算法: crc16(default) and crc32
rep: hex #进制: dec(default) and hex

会自动生成形如以下的格式:

    crc16 & hex
    https://post.zz173.com/posts/66c8.html

    crc16 & dec
    https://post.zz173.com/posts/65535.html

    crc32 & hex
    https://post.zz173.com/posts/8ddf18fb.html

    crc32 & dec
    https://post.zz173.com/posts/1690090958.html

第二种为每篇文章手动指定abbrlink的方式,在需要指定文章的Front-Matter中加入abbrlink: 1即可。

第三种是指定文章开始的id,之后按数字逐次添加,但这样大批量生成文章时id很容易重复,不建议使用此方法。

站点地图与搜索引擎收录

通过设置站点地图,可以便捷地让各大搜索引擎收录站点内容,同时也可以避免一些不想公开在搜索引擎的内容被自动收录。新建的GitHub Pages或其他新的站点短时间内一般不会被谷歌检索,但长时间可能会被谷歌的爬虫爬到一些内容 而百度倒不用担心毕竟Github Pages部分早已屏蔽百度索引,所以需尽早向搜索引擎提交站点地图(其实不提交搜索引擎最后也会索引到站点地图)。

建立站点地图需要安装相关插件。对于谷歌和必应等国外搜索引擎,安装hexo-generator-sitemap插件,对百度则安装hexo-generator-baidu-sitemap插件。

1
2
npm install hexo-generator-sitemap --save
npm install hexo-generator-baidu-sitemap --save

在站点配置文件_config.yml中设置站点地图参数。

1
2
3
4
5
6
sitemap:
path: sitemap.xml # 站点地图文件生成
template: ./sitemap_template.xml # 站点地图文件所采用的模板目录,没有建议删除
rel: false # 添加 rel-sitemap 在每个网页的 header.
tags: true # Add site's tags
categories: true # Add site's categories

如果不希望某个页面被站点地图收录,可在每个文章或页面.md文件的Front-Matter添加sitemap: false

谷歌收录

前往GoogleSearchConsole,按照网站引导添加站点地图。至于Github Pages验证方式,如果主题支持按照主题的引导,如果主题不支持,只能使用HTML标记法验证所有权了。相比之下,有个域名要轻松得多。

必应收录

前往Bing Webmaster Tools提交,如果之前已经在谷歌提交的话,必应支持谷歌直接导入,验证所有权会方便许多,当然站点地图还是要手动提交。

百度收录

比较麻烦,建议直接百度。

建立不受主题渲染的页面

方法一

使用 Hexo 提供的跳过渲染配置,在站点的配置文件 _config.yml 里找到 skip_render 关键字,在后面添加想要跳过渲染的页面,比如创建 \source\about\index.html, 配置文件填写:skip_render: about\**,那么就表示 \source\about 里所有的文件将跳过渲染,里面的文件将会被直接复制到 public 文件夹,此时就会得到一个独立的 about 页面。

方法二

在文章头部的 Front-matter 里添加配置 layout: false 来跳过渲染配置,比如我们要使 about 页面跳过渲染,创建 \source\about\index.md,将这个页面的相关 HTML 代码写进.md文件并保存。

压缩静态资源

由于静态站点,相关文件很多,加载起来比较费时间。gulp能够帮助用户自动压缩静态资源,配合各类下属插件,能够压缩包括css、js、html乃至各类格式的图片文件。

首先安装gulp的主要插件。

1
2
npm install --global gulp-cli #全局安装gulp指令集
npm install gulp --save #安装gulp插件

安装下属的压缩插件。目前图片压缩效果不佳,暂时放弃。

1
2
3
4
npm install gulp-htmlclean --save-dev #压缩html
npm install gulp-html-minifier-terser --save-dev # 用gulp-html-minifier-terser可以压缩HTML中的ES6语法
npm install gulp-clean-css --save-dev #压缩css
npm install gulp-terser --save-dev #压缩js,但会失去IE等老旧浏览器的兼容性

安装完成后,在站点根目录下新建并编写gulpfile.js任务脚本,此处直接照搬了Akilar的示例,字体压缩仅支持.tff文件,本站没有采用压缩字体部分的内容。

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
//用到的各个插件
var gulp = require('gulp');
var cleanCSS = require('gulp-clean-css');
var htmlmin = require('gulp-html-minifier-terser');
var htmlclean = require('gulp-htmlclean');
var fontmin = require('gulp-fontmin');
// gulp-tester
var terser = require('gulp-terser');
// 压缩js
gulp.task('compress', async() =>{
gulp.src(['./public/**/*.js', '!./public/**/*.min.js'])
.pipe(terser())
.pipe(gulp.dest('./public'))
});
//压缩css
gulp.task('minify-css', () => {
return gulp.src(['./public/**/*.css'])
.pipe(cleanCSS({
compatibility: 'ie11'
}))
.pipe(gulp.dest('./public'));
});
//压缩html
gulp.task('minify-html', () => {
return gulp.src('./public/**/*.html')
.pipe(htmlclean())
.pipe(htmlmin({
removeComments: true, //清除html注释
collapseWhitespace: true, //压缩html
collapseBooleanAttributes: true,
//省略布尔属性的值,例如:<input checked="true"/> ==> <input />
removeEmptyAttributes: true,
//删除所有空格作属性值,例如:<input id="" /> ==> <input />
removeScriptTypeAttributes: true,
//删除<script>的type="text/javascript"
removeStyleLinkTypeAttributes: true,
//删除<style>和<link>的 type="text/css"
minifyJS: true, //压缩页面 JS
minifyCSS: true, //压缩页面 CSS
minifyURLs: true //压缩页面URL
}))
.pipe(gulp.dest('./public'))
});
//压缩字体
function minifyFont(text, cb) {
gulp
.src('./public/fonts/*.ttf') //原字体所在目录
.pipe(fontmin({
text: text
}))
.pipe(gulp.dest('./public/fontsdest/')) //压缩后的输出目录
.on('end', cb);
}

gulp.task('mini-font', (cb) => {
var buffers = [];
gulp
.src(['./public/**/*.html']) //HTML文件所在目录请根据自身情况修改
.on('data', function(file) {
buffers.push(file.contents);
})
.on('end', function() {
var text = Buffer.concat(buffers).toString('utf-8');
minifyFont(text, cb);
});
});
// 运行gulp命令时依次执行以下任务
gulp.task('default', gulp.parallel(
'compress', 'minify-css', 'minify-html','mini-font'
))

Akilar对字体压缩``font-min的补充说明,在本文中,是通过读取所有编译好的html文件(./public/**/.html)中的字符,然后匹配原有字体包内./public/fonts/.ttf字体样式,输出压缩后的字体包到./public/fontsdest/目录。所以最终引用字体的相对路径应该是/fontsdest/*.ttf`。而本地测试时,如果没有运行gulp,自然也就不会输出压缩字体包到public目录,也就看不到字体样式。

在每次运行完hexo generate生成静态页面后,运行gulp命令对站点根目录\public目录下的文件进行压缩,建议先在本地调试。

NExT主题自定义样式记录

万一以后用得上呢~

实现置顶效果

网络上已经有无数的文章说到了如何在NExT中实现文章置顶,首先安装hexo-generator-index-pin-top插件:

1
2
npm uninstall hexo-generator-index --save
npm install hexo-generator-index-pin-top --save

然后在需要置顶的文章的Front-matter中加上top: true即可。

但问题在于,置顶的文章并没有置顶标签,需要在站点根目录\source\_data\post_meta.njk中添加自定义样式:

1
2
3
4
5
{% if post.top %}
<i class="fa fa-thumb-tack"></i>
<font color=7D26CD>置顶</font>
<span class="post-meta-divider">|</span>
{% endif %}

问题在于这样加入的置顶标签会失去间隔效果,但解决的代码我忘记保存了……

侧边栏添加近期文章

\source\_data\sidebar.njk

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{% if theme.recent_posts %}
<div class="links-of-blogroll motion-element {{ "links-of-blogroll-" + theme.recent_posts_layout }}">
<div class="links-of-blogroll-title">
<!-- 选择合适的icon -->
<i class="fa fa-history fa-{{ theme.recent_posts_icon | lower }}" aria-hidden="true"></i>
<!-- 这里对应下文主题配置文件的recent_posts_title值 -->
{{ theme.recent_posts_title }}
</div>
<ul class="links-of-blogroll-list">
<!-- 设置排序规格,此处我采用按照文章更新时间排序 -->
{% set posts = site.posts.sort('-updated').toArray() %}
<!-- 显示三条近期文章,请自信合理配置 -->
{% for post in posts.slice('0', '3') %}
<li>
<a href="{{ url_for(post.path) }}" title="{{ post.title }}" target="_blank">{{ post.title }}</a>
</li>
{% endfor %}
</ul>
</div>
{% endif %}
文章分享

安装插件hexo-next-share。

1
npm install theme-next/hexo-next-share --save

在主题配置文件添加如下内容,之后自定义需要添加的分享按钮。

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
# NeedMoreShare2
# Dependencies: https://github.com/theme-next/theme-next-needmoreshare2
# For more information: https://github.com/revir/need-more-share2
# iconStyle: default | box
# boxForm: horizontal | vertical
# position: top / middle / bottom + Left / Center / Right
# networks:
# Weibo | Wechat | Douban | QQZone | Twitter | Facebook | Linkedin | Mailto | Reddit | Delicious | StumbleUpon | Pinterest
# GooglePlus | Tumblr | GoogleBookmarks | Newsvine | Evernote | Friendfeed | Vkontakte | Odnoklassniki | Mailru
needmoreshare:
enable: true
cdn:
js: //cdn.jsdelivr.net/gh/theme-next/theme-next-needmoreshare2@1/needsharebutton.min.js
css: //cdn.jsdelivr.net/gh/theme-next/theme-next-needmoreshare2@1/needsharebutton.min.css
postbottom:
enable: true
options:
iconStyle: box
boxForm: horizontal
position: bottomCenter
networks: Weibo,Wechat,Douban,QQZone,Twitter,Facebook
float:
enable: false
options:
iconStyle: box
boxForm: horizontal
position: middleRight
networks: Weibo,Wechat,Douban,QQZone,Twitter,Facebook

footer加入运行时间

\source\_data\sidebar.njk。这个在其他主题同样可用,如果是Butterfly主题,直接在_config.butterfly.yml相应设置项添加即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!-- 网站运行时间的设置 -->
<div id="time">
<span id="timeDate">载入天数...</span>
<span id="times">载入时分秒...</span>
<script>
var now = new Date();
function createtime() {
var grt= new Date("02/16/2016 12:01:00");//此处修改你的建站时间,注意格式
now.setTime(now.getTime()+250);
days = (now - grt ) / 1000 / 60 / 60 / 24; dnum = Math.floor(days);
hours = (now - grt ) / 1000 / 60 / 60 - (24 * dnum); hnum = Math.floor(hours);
if(String(hnum).length ==1 ){hnum = "0" + hnum;} minutes = (now - grt ) / 1000 /60 - (24 * 60 * dnum) - (60 * hnum);
mnum = Math.floor(minutes); if(String(mnum).length ==1 ){mnum = "0" + mnum;}
seconds = (now - grt ) / 1000 - (24 * 60 * 60 * dnum) - (60 * 60 * hnum) - (60 * mnum);
snum = Math.round(seconds); if(String(snum).length ==1 ){snum = "0" + snum;}
document.getElementById("timeDate").innerHTML = "小站在 Github 服务器上托管了 "+dnum+" 天 ";
document.getElementById("times").innerHTML = hnum + " 小时 " + mnum + " 分 " + snum + " 秒";
}
setInterval("createtime()",250);
</script>
</div>

Volantis主题自定义样式记录

文章页首行缩进开关

文字类尤其是以中文为主的文章最好还是开启首行缩进,但也不是所有文章,比如一些技术文章都适合首行缩进,此处是做了一个开关,通过 page.indent 控制是否首行缩进。

修改 article.ejs,在最前面 articleclass 中添加对应样式:

1
<article class="<%- page.indent == true ? 'cus-indent' : '' %> article post white-box reveal md <%- theme.custom_css.body.effect.join(' ') %> article-type-<%= post.layout %>" id="<%= post.layout %>" itemscope itemprop="blogPost">

在自定义CSS样式文件中添加以下内容:

1
2
3
4
5
6
7
8
9
10
11
#article-container
p{
text-indent: 2em;
}
#article-container detail p{ text-indent: 0em !important; }
#article-container blockquote p{ text-indent: 0em !important; }
#article-container section p{ text-indent: 0em !important; }
#article-container .prev-next p{ text-indent: 0em !important; }
#article-container .new-meta-box p{ text-indent: 0em !important; }
#article-container .note p{ text-indent: 0em !important; }
#article-container .tag p{ text-indent: 0em !important; }

Butterfly主题自定义样式记录

Tag Plugins Plus 插件

参见Butterfly 主题外挂标签的语法手册

增加站点数据统计

使用插件hexo-butterfly-chartsGithub地址)。
npm i hexo-butterfly-charts --save

在主题配置文件_config.butterfly.yml 或站点配置文件_config.yml 中增加以下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
# 统计图表,支持发布文章统计、发布日历、Top标签统计、分类统计、分类雷达。
# see https://www.npmjs.com/package/hexo-butterfly-charts
charts:
enable: true # 是否启用功能
postsChart:
title: 文章发布统计 # 设置文章发布统计的标题,默认为空
interval: 1 # 横坐标间隔
tagsChart:
title: Top 10 标签统计 # 设置标签统计的标题,默认为空
interval: 1 # 横坐标间隔
postsCalendar_Title: #文章发布日历 # 设置发布日历的标题,默认为空
categoriesChart_Title: # 设置分类统计的标题,默认为空
categoriesRadar_Title: # 设置分类雷达的标题,默认为空

本站在归档、分类、标签页面使用了其中部分图表,所有图表放在更新记录页面。其中:

  • 归档页面:发布日历。在 theme/Butterfly/layout/archive.pug 文件中,#archive 下面添加一行#posts-calendar注意缩进
  • 分类页面:在/Butterfly/layout/includes/page/categories.pug文件中,第 5 行添加#categories-chart,修改后的文件如下所示:
1
2
3
4
5
6
7
.category-lists
.category-title.is-center= _p('page.category')
| -
span.category-amount= site.categories.length
#categories-chart
#categories-radar
div!= list_categories()
  • 标签页面:标签统计。在theme/Butterfly/layout/includes/page/tags.pug 文件中,开头添加一行#tags-chart(data-length = "10"),data-length = "10",表示显示 Top 10 的标签。

分类和标签页面导航栏

原版Butterfly主题的分类和标签页面比较简陋,而且查看不便,尤其是标签页面没有展示数字,因此为其增加了导航栏和标签数。

首先介绍如何在原版标签页面增加标签数。步骤非常简单,通过检查标签页生成代码,可以发现标签绘制使用了cloudTags函数,在文件夹搜索cloudTags,可以看到标签云渲染的代码在站点根目录\themes\butterfly\scripts\helpers\page.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
hexo.extend.helper.register('cloudTags', function (options = {}) {
const theme = hexo.theme.config
const env = this
let source = options.source
const minfontsize = options.minfontsize
const maxfontsize = options.maxfontsize
const limit = options.limit
const unit = options.unit || 'px'

let result = ''
if (limit > 0) {
source = source.limit(limit)
}

const sizes = []
source.sort('length').forEach(tag => {
const { length } = tag
if (sizes.includes(length)) return
sizes.push(length)
})

const length = sizes.length - 1
source.forEach(tag => {
const ratio = length ? sizes.indexOf(tag.length) / length : 0
const size = minfontsize + ((maxfontsize - minfontsize) * ratio)
let style = `font-size: ${parseFloat(size.toFixed(2))}${unit};`
const color = 'rgb(' + Math.floor(Math.random() * 201) + ', ' + Math.floor(Math.random() * 201) + ', ' + Math.floor(Math.random() * 201) + ')' // 0,0,0 -> 200,200,200
style += ` color: ${color}`
- result += `<a href="${env.url_for(tag.path)}" style="${style}">${tag.name}</a>`
+ result += `<a href="${env.url_for(tag.path)}" style="${style}">${tag.name}<sup>${tag.length}</sup></a>`
})
return result
})

使用<sup>${tag.length}</sup><sub>${tag.length}</sub> 可绘制表示标签文章数的上下标。

接下来是为分类和标签页添加导航条,方便从一个标签页快捷跳转到另一个标签页。在站点根目录\themes\butterfly\scripts\helpers\ 目录下新建文件 catalog_list.js,代码如下所示。

1
2
3
4
5
6
7
8
9
10
11
hexo.extend.helper.register('catalog_list', function (type) {
let html = ``
hexo.locals.get(type).map(function (item) {
html += `
<div class="catalog-list-item" id="/${item.path}">
<a href="/${item.path}">${item.name}<sup>${item.length}</sup></a>
</div>
`
})
return html
})

其中,type 参数表示生成 分类导航栏 categories 还是 标签导航栏 tags,其中 <sup>${item.length}</sup> 是使用上标显示文章数量。

接下来是为导航条新增自定义样式,样式需要修改其中 --main--second--card-border rgba(150,150,150,0.2);--light-text--border-radius字段为自己偏好的设计。样式如下所示:

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
/* 分类目录条、标签目录条 */
#catalog-bar {
padding: .4rem .8rem;
border-radius: var(--border-radius);
display: flex;
border: var(--card-border);
margin-bottom: 1rem;
justify-content: space-between;
}

#catalog-bar:hover {
border-color: var(--main);
}

#catalog-bar i {
line-height: inherit;
}

#catalog-list {
/* 分类/标签较少时,可以选择不设置 width,居中显示 catalog-list-item */
/* width: 100%; */
margin: 0 .5rem;
display: flex;
white-space: nowrap;
overflow-x: scroll;
}

#catalog-list::-webkit-scrollbar {
display: none;
}

.catalog-list-item a {
margin: 0 .2em;
padding: 0.2em 0.3em 0.3em;
font-weight: bold;
border-radius: var(--border-radius);
color: var(--font-color);
-webkit-transition: all .3s ease-in-out;
-moz-transition: all .3s ease-in-out;
-o-transition: all .3s ease-in-out;
-ms-transition: all .3s ease-in-out;
transition: all .3s ease-in-out;
}

.catalog-list-item:hover a {
background: var(--main);
color: var(--second);
}

.catalog-list-item.selected a {
background: var(--light-text);
color: var(--second);
}

a.catalog-more {
min-width: fit-content;
font-weight: bold;
color: var(--font-color);
}

a.catalog-more:hover {
color: var(--main);
}

样式添加完成后,导航条基本成型,但还需要优化一下体验,做到在导航条中自动定位并高亮当前正在查看的分类或目录。在站点根目录\source\js\目录下建立自定义JS,添加如下内容,可随意命名,之后在_config.butterfly.yml设置引入该文件,即可重新生成预览查看效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function catalogActive () {
let $list = document.getElementById('catalog-list')
if ($list) {
// 鼠标滚轮滚动
$list.addEventListener('mousewheel', function (e) {
// 计算鼠标滚轮滚动的距离
$list.scrollLeft -= e.wheelDelta / 2
// 阻止浏览器默认方法
e.preventDefault()
}, false)

// 高亮当前页面对应的分类或标签
let $catalog = document.getElementById(decodeURIComponent(window.location.pathname))
$catalog.classList.add('selected')

// 滚动当前页面对应的分类或标签到中部
$list.scrollLeft = ($catalog.offsetLeft - $list.offsetLeft) - ($list.offsetWidth - $catalog.offsetWidth) / 2
}
}
catalogActive()

Github Action 自动部署

站点部署后,文件由于放在电脑本地,这样如果在外面或需要用移动端进行编辑的时候,就因为无法部署到远程仓库而只能等待下次使用电脑,很不方便。通过Github Action可以将站点文件的部署流程也一起提交到远端,这样部署就不会受到设备的限制,随时随地都可以提交改动。

要实现自动部署,我们需要:

  • 一个 GitHub Pages 仓库
  • 一个 Hexo 站点备份仓库/分支
  • GitHub Personal Access Token 用于获取仓库的推送权限

为了方便管理和保护隐私,我另外创建了一个仓库用于管理站点文件,这样我的文章尤其是加密的文章就不会因为Github Pages公开而一起公开。直接将本地的站点文件复制到另一个文件夹,用Github Desktop程序设定新文件夹为新仓库的本地文件。由于事先设定了SSH Key并指定了唯一仓库,在新的文件夹也可以执行原来的Hexo命令。当然使用原来的分支也可以,只是在最后 push 的时候分支设置略有区别。

不管文件目录在哪,一般Hexo站点文件最少都应包括这些文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
│ package.json
│ .gitignore
│ _config.yml
├─scaffolds
│ draft.md
│ page.md
│ post.md
├─source
│ ├─xxxxxx
│ ├─xxxxxx
│ └─_posts
│ ├─xxxxxx
│ └─xxxxxx
└─themes(或node_modules下对应的主题文件)
└─xxxxxx

如果之前安装了其他组件,要求在文件夹根目录建立文件,则这些文件也要推送,如hexo-generator-search,需要推送根目录的search.xml

设置 GitHub Personal Access Token

点击 GitHub 用户设置页面 最下方的Developer setting,然后选择Personal access tokens来生成一个 token,由于我们只需要能够对普通仓库 push (推送)就行了,所以把Select scopesrepo部分勾上即可。生成的 token 只会显示一次,所以一定要及时保存下来,否则就只能删除重新创建了。

新GitHub Personal Access Token生成页面,如图选择使用范围

在Github网页,找到Hexo站点文件仓库,在上方的Settings,点到左侧的 Secrets 项,添加两个秘密环境变量 GH_REF 、GH_TOKEN,值分别填写自己的 GitHub Pages 仓库地址(不包含https://)和刚刚申请到的 GitHub Personal Access Token。

配置完成后Secrets项会显示GH_REF和GH_TOKEN两个变量

配置 Github Action

设置触发条件和工作环境

我设置在 hexo 分支上发生 push 操作时触发构建,使用最新的 Ubuntu 系统作为编译部署的环境,同时设置一个全局环境变量将时区修改为 Asia/Shanghai ,具体的配置内容如下:

之所以先设定时区,是因为 Github Action 自动部署提交 commit 的时区默认为美国太平洋时间。如果按 :year/:i_month:day-:id 的格式组织文章链接会导致文章生成错误(因为填写时间一般都是按所在地时间来),因此自动部署后如果经常在东西半球旅行写文章,需要特别注意时区。

然后检出代码,设置 node 环境,我们这里使用 12.x 版本的 node.js,一些插件并未适配最新版本的 node.js,所以设置为更稳定的12.x。

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
name: Github Pages CI/CD

# Controls when the action will run.
on:
# Triggers the workflow on push or pull request events but only for the hexo branch
push:
branches: hexo # 这个分支名需要按照你的设置来

env:
GIT_USER: Github用户名
GIT_EMAIL: Github账号当前使用的邮箱
TZ: Asia/Shanghai # 必填,时间,建议为你常住地的时间

jobs:
blog-cicd:
name: Hexo blog build & deploy
runs-on: ubuntu-latest

steps:
- name: Checkout codes
uses: actions/checkout@v2

- name: Setup node
uses: actions/setup-node@v1
with:
node-version: '12.x'

生成和部署文件

由于每次运行 Action 流程都在服务器上进行,因此需要安装依赖和插件。如果使用了 gulp 任务来压缩 Hexo 生成的文件,gulp 安装命令也一并加上,没有的话就别加以免浪费部署时间。

1
2
3
4
- name: Install dependencies
run: |
npm install hexo-cli gulp -g
npm install

环境配置好后,就和我们通常的部署流程大同小异了。

1
2
3
4
5
- name: Generate files
run: hexo generate

- name: Execute gulp task
run: gulp

部署时有几点需要注意的,我在这里翻车了很多次。

首先自动部署是云端进行的,需要先将 GitHub Pages 仓库克隆过来,将其中的 .git 目录移到存放部署文件的 public 目录中(为了保留 GitHub Pages 仓库的提交历史)。

为了防止 git 服务器出现没有适当的证书这种情况,需要关闭证书验证,然后再进行克隆。当然这样安全性会降低一些但其实也没什么影响。

git config http.sslVerify "false" 
git clone "https://${{ secrets.GH_REF }}" deploy_git

由于仓库不知为何变成了浅层克隆,出现shallow update not allowed因此需要以下命令拉取所有内容。

git fetch --unshallow origin

最后使用 git 的提交命令提交即可。提交后Github会自动进行一次测试以检验是否有效,如果部署失败会自动发送一封邮件通知。

我的最终文件如下:

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
name: Github Pages CI/CD

on:
push:
branches: hexo

env:
GIT_USER: Github用户名
GIT_EMAIL: Github账号使用的邮箱
TZ: Asia/Shanghai

jobs:
blog-cicd:
name: Hexo pages build & deploy
runs-on: ubuntu-latest

steps:
- name: Checkout codes
uses: actions/checkout@v2

- name: Setup node
uses: actions/setup-node@v1
with:
node-version: '12.x'

- name: Install dependencies
run: |
npm install hexo-cli -g
npm install
- name: Generate files
run: |
hexo generate
hexo douban

- name: Hexo deploy
run: |
git config --global http.sslVerify false
git clone "https://${{ secrets.GH_REF }}" deploy_git
git fetch --unshallow origin
mv ./deploy_git/.git ./public/
cd ./public
git config --global user.name $GIT_USER
git config --global user.email $GIT_EMAIL
git config user.name "自动部署提交的用户名义,随便写"
git config user.email "自动部署提交的用户名义的邮箱,随便写"
git add .
git commit -m "GitHub Actions Auto Builder at $(date +'%Y-%m-%d %H:%M:%S')"
git push --force --quiet "https://${{ secrets.GH_TOKEN }}@${{ secrets.GH_REF }}" main:main

奇特的报错

Accessing non-existent property ‘xxx’ of module exports inside circular dependency

运行hexo s时出现如下信息,但不影响程序运行。

1
2
3
4
5
6
7
8
$ hexo s                              
(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)
(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

原因是 node.js 14 后,Hexo 的相关依赖没有更新。

解决方法:重装hexo-renderer-stylus(stylus在0.54.8以上)和nibnib好像有更新到1.1.3,但没有新发行版,可以直接从Github上拉取,然后修改package.jsonpackage-lock.json文件的版本即可。

配置自定义域名

购买域名

随便选一个域名购买平台,如阿里云、腾讯云、万网等。购买域名后等待一段时间,并实名认证、完善SSL证书等手续后,域名商会提供DNS设置网站,没什么好说的。

配置域名

使用Github Pages重定向

在 Poweshell 使用 ping 命令到需要定向的Github Pages,得到它的IP地址,然后在DNS解析设置中设置主机记录@,记录类型为A,记录值填写为刚刚得到的IP地址,然后新建另一个解析记录。设置主机记录为www,进行相同的设置。

在Github存储了Github Pages的仓库,进入 Settings 标签页,在 Pages 菜单中,将刚刚申请的域名写进去。回到仓库,在根目录下创建名为CNAME的文件,进入编辑,添加购买的域名。

使用Vercel加速

Vercel账号注册在之前配置Waline评论时已经说明了不过这一段还没写,此处不再重复。

新建项目,选择Import Git Repository,在Enter the URL of a Git repository to deploy it,填写Github Pages仓库地址。之后Vercel会在Github账号上安装自动部署应用,通过这个,仓库如果有更新,Vercel就会自动获取更新并部署,建议选 All repositories,以后如果还要用Vercel部署网站就不用再次安装。

接下来配置Vercel项目,项目一旦确定部署后无法更改。之后全部选择默认,一路快进到自动部署完毕。部署完成后即可使用Vercel提供的默认域名来访问静态页面,不过这个域名很长没多少人用。

进入部署完成的新Vercel项目,在Overview菜单下,点击Domains,在右侧输入框里填写购买的域名。域名添加后Vercel会提供两个解析记录,只需要将其中一个解析记录添加到域名的DNS解析设置即可(视供应商不同,生效时间可能长达 48 小时)[3]。如果测试成功,可以关闭指向原来 username.github.io 的解析记录了,之后我们开的是“Vercel Pages ”而不是 Github Pages ,所以 Github Pages 也没必要在开着了,或者拿来搞点别的静态网站也没问题。


图源:Akilar

使用Coding双向部署和加速

要钱的,没有备案也加不了速,别想了参见使用 Coding 和 Github 来实现双线部署


参考资料

  1. Hexo文档
  2. NexT 使用文档,iissnan
  3. Butterfly 安裝文檔Jerryc127
  4. git window下配置SSH连接GitHub天才小小布
  5. 提升 RSS 体验:Hexo 博客 Feed 指北Nozomi 自留地
  6. hexo 主题 next7.8 版本配置美化Kali 的 IT 博客
  7. 使用 GitHub Actions 自动构建 Hexo 博客
  8. 使用 Vercel 来加速 Hexo 博客[3:1],Akilar
  9. Sidebar Artitalk,Akilar,参考了其配置项
  10. Github+jsDelivr+PicGo 打造稳定快速、高效免费图床,ITBOB
  11. HexoPlusPlus,ChenYFan,2X-ercha,oCoke,so1ve 等
  12. 使用 Charts 插件给 Butterfly 增加统计图表,Guo Le
  13. 解决 Hexo 在使用 Node.js 14 时的 Accessing non-existent property ‘xxx’ of module exports inside circular dependency 问题好一则博
  14. 使用gulp压缩博客静态资源,Akilar
  15. Butterfly 标签云增加文章数上下标,Eurkon
  16. Butterfly 分类标签导航栏,Eurkon

  1. 在安装了hexo-douban插件后,这个指令就不可用了。 ↩︎

  2. 它的作用是,在图片上传后,PicGo 会按照自定义域名+储存路径+上传的图片名的方式生成访问链接,所以可以设置为 https://cdn.jsdelivr.net/gh/用户名/图床仓库名 。如果需要引用仓库资源,则链接为 https://cdn.jsdelivr.net/gh/你的用户名/你的仓库名@发布的版本号/文件路径↩︎

  3. 2021年5月14日Vercel服务在中国大陆被屏蔽,原方法对中国大陆访问已不适用。官方提供了解决方案:修改 A 记录,从 76.76.21.21 更改为 76.223.126.88;修改 CNAME,从 cname.vercel-dns.com 更改为 cname-china.vercel-dns.com。屏蔽信息参见 https://www.vercel-status.com/incidents/r758bhbklgfd↩︎ ↩︎