Build Blog with Hexo

1. Git 安装

1.1 Git Bash 和 CMD

注意, Git Bash 和 CMD 不全相同

CMD是Windows自带的命令解释器, 是Windows系统内置的

Git Bash是一个在 Windows 上模拟 Linux/Unix shell 环境 的终端, 是基于 MSYS2/MinGW(第三方 Unix-like 兼容层)的.

简单来说, CMD是Windows系统使用的, Git Bash是在Windows上模拟Unix系统的终端. 由于我比较熟悉Linux环境下的操作, 所以我会尽量多的使用 Git Bash, 对Git Bash有更多的要求.

1.2 安装Git Bash

Git官网下载安装包,开始安装程序(管理员模式).

勾选Add a Git Bash profile to Windows Terminal, 在 Windows Terminal 中自动添加一个“Git Bash”启动项(Profile),可以一键打开 Git Bash 终端.

其余选项不动, 安装

1.3 Git 配置

打开Git Bash

配置全局设置用户名和邮箱:

1
2
git config --global user.name "Your Name"
git config --global user.email "email@example.com"

用下面命令查看配置:

1
git config --global --list

下面配置SSH公钥:

配置了ssh公钥之后, 本机push到github的时候就不需要登录了

  1. 检查是否已存在ssh key, 密钥不存在

    1
    2
    cd ~/.ssh
    bash: cd: /c/Users/test/.ssh: No such file or directory
  2. 生成密钥(执行后一直回车即可)

    1
    ssh-keygen -t rsa -C "xxx@xxx.com"
  3. 获取ssh key公钥内容(id_rsa.pub)

    1
    2
    cd ~/.ssh
    cat id_rsa.pub
  4. Github账号上添加公钥

    头像->setting->SSH and GPG keys-> New SSH key

    然后添加新的ssh密钥即可

  5. 使用ssh -T git@github.com验证

    1
    2
    3
    test@ѩ▒ MINGW64 ~/.ssh
    $ ssh -T git@github.com
    Hi AskemiWang! You've successfully authenticated, but GitHub does not provide shell access.

1.4 题外话 CMD, Power Shell and Git Bash

ls是unix命令, Windows的CMD并不支持, power shell为了兼容所以能用. 而tree命令这种Windows原生自带的, 两者都可以使用, 但是git bash使用不了.

因为git bash只能使用unix的tree命令, 但是我们Windows上的git bash是基于 MSYS2(Minimal SYStem 2),提供一个类 Unix 环境。

Git for Windows(即 Git Bash)只打包了运行 Git 所需的最小工具集。这是一个轻量化的MSYS2, 并不完整, 所以很多Unix上的工具, 在git bash上无法使用.

2. Nodejs 安装

2.1 Nodejs 安装

Hexo是基于Node JS框架开发的, 所以先安装Node JS

Nodejs安装包下载

下载完毕开始安装, 会自动安装nodejs和npm, 安装完后检查:

1
2
3
4
5
6
7
test@▒ѩ▒ MINGW64 ~
$ node -v
v24.13.0

test@ѩ▒ MINGW64 ~
$ npm -v
11.6.2

可以看到版本信息, 已经安装好了.

2.2 环境变量配置

先打开nodejs文件夹的位置, 新建两个文件夹node_cache和node_global

1
D:\softwares\nodejs

用powershell可以查看到:

1
ls | ? Name -like "*node*"
1
2
3
4
5
6
7
8
9
10
11
12
13
PS D:\softwares\nodejs> ls | ? Name -like "*node*"


目录: D:\softwares\nodejs


Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 2026/1/19 5:01 node_cache
d----- 2026/1/19 12:23 node_global
d----- 2026/1/19 4:40 node_modules
-a---- 2026/1/12 18:55 89953280 node.exe
-a---- 2024/11/7 15:33 702 nodevars.bat

注意, ls是unix命令, Windows的CMD并不支持, power shell为了兼容所以能用. 而tree命令这种Windows原生自带的, 两者都可以使用, 但是git bash使用不了.

因为git bash只能使用unix的tree命令, 但是我们Windows上的git bash是基于 MSYS2(Minimal SYStem 2),提供一个类 Unix 环境。

Git for Windows(即 Git Bash)只打包了运行 Git 所需的最小工具集。这是一个轻量化的MSYS2, 并不完整, 所以很多Unix上的工具, 在git bash上无法使用.

用管理员权限配置:

1
2
npm config set prefix "D:\softwares\nodejs\node_global"
npm config set cache "D:\softwares\nodejs\node_cache"

get命令检查:

1
2
3
4
5
6
7
8
test@▒ѩ▒ MINGW64 /d/softwares/nodejs
$ npm config get prefix
D:\softwares\nodejs\node_global

test@▒ѩ▒ MINGW64 /d/softwares/nodejs
$ npm config get cache
D:\softwares\nodejs\node_cache

打开系统的环境变量

  1. 系统变量->新建:

    变量名: NODE_PATH

    变量值: D:\softwares\nodejs\node_global\node_modules

    注意, 变量值后面加了一级子目录 node_modules

  2. 用户变量中Path项->编辑->新建:

    添加项: D:\softwares\nodejs\node_global

  3. 系统变量->编辑->新建:

    添加项: %NODE_PATH%

检查(记得开管理员模式):

1
2
3
4
5
6
7
8
test@▒ѩ▒ MINGW64 ~
$ npm install express -g

changed 65 packages in 2s

22 packages are looking for funding
run `npm fund` for details

3. Hexo 安装

3.1 安装Hexo命令行工具

安装命令:

1
npm install -g hexo-cli

check:

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
test@▒ѩ▒ MINGW64 ~
$ hexo -v
hexo-cli: 4.3.2
os: win32 10.0.26100 undefined
node: 24.13.0
acorn: 8.15.0
ada: 3.3.0
amaro: 1.1.5
ares: 1.34.6
brotli: 1.1.0
cjs_module_lexer: 2.1.0
cldr: 47.0
icu: 77.1
llhttp: 9.3.0
modules: 137
napi: 10
nbytes: 0.1.1
ncrypto: 0.0.1
nghttp2: 1.67.1
openssl: 3.5.4
simdjson: 4.1.0
simdutf: 6.4.0
sqlite: 3.50.4
tz: 2025b
undici: 7.18.2
unicode: 16.0
uv: 1.51.0
uvwasi: 0.0.23
v8: 13.6.233.17-node.37
zlib: 1.3.1-470d3a2
zstd: 1.5.7

3.2 创建一个Blog项目

创建文件夹:

1
mkdir myBlog && cd myBlog

初始化hexo项目:

1
hexo init

根据 package.json 安装项目所需的所有依赖包:

1
npm install

生成测试博客并启动本地服务器:

hexo g

hexo s

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
test@ѩ▒ MINGW64 /e/myBlog
$ hexo g
INFO Validating config
INFO Start processing
INFO Files loaded in 361 ms
INFO Generated: archives/index.html
INFO Generated: archives/2026/index.html
INFO Generated: archives/2026/06/index.html
INFO Generated: index.html
INFO Generated: css/style.css
INFO Generated: js/jquery-3.6.4.min.js
INFO Generated: fancybox/jquery.fancybox.min.css
INFO Generated: js/script.js
INFO Generated: fancybox/jquery.fancybox.min.js
INFO Generated: css/images/banner.jpg
INFO Generated: 2026/06/24/hello-world/index.html
INFO 11 files generated in 391 ms

test@ѩ▒ MINGW64 /e/myBlog
$ hexo s
INFO Validating config
INFO Start processing
INFO Hexo is running at http://localhost:4000/ . Press Ctrl+C to stop.

访问:

本地服务器

3.3 创建一个特殊的GitHub仓库

GitHub上创建一个新仓库, 名字为

1
YourGitHubName.github.io

比如:

1
AskemiWang.github.io

这个仓库为什么特殊?

GitHub 对名为 用户名.github.io 的仓库有特殊待遇,它被视为你的用户站点(User Site)。GitHub可以自动自动启用 GitHub Pages服务, 简单来说方便提供网页服务.

一定要将可见性设置为Public

3.4 将Hexo部署到GitHub

  1. 修改_config.yml文件的deploy部分, 原来的样子是这样的:

    1
    2
    3
    4
    # Deployment
    ## Docs: https://hexo.io/docs/one-command-deployment
    deploy:
    type: ''

    修改为:

    1
    2
    3
    4
    5
    6
    # Deployment
    ## Docs: https://hexo.io/docs/one-command-deployment
    deploy:
    type: git
    repo: git@github.com:AskemiWang/AskemiWang.github.io.git
    branch: main
  2. 修改URL:

    1
    2
    3
    # URL
    ## Set your site url here. For example, if you use GitHub Page, set url as 'https://username.github.io/project'
    url: https://AskemiWang.github.io

    注意, 仓库地址最好用SSH的

3.5 安装hexo-deployer-git插件

这个插件的作用是帮我们更新博客, 推送到github上(相当于集成了git add, git push等命令)

1
npm install hexo-deployer-git --save

3.6 推送博客到GitHub

每次推送前需要的步骤:

  • 清除前一次生成的文件
  • 生成新的文件
  • 部署
1
2
3
hexo clean
hexo generate
hexo deploy

用一条命令解决:

1
hexo clean && hexo g -d
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
test@ѩ▒ MINGW64 /e/myBlog
$ hexo clean && hexo g -d
INFO Validating config
INFO Deleted database.
INFO Deleted public folder.
INFO Validating config
INFO Start processing
INFO Files loaded in 67 ms
INFO Generated: archives/index.html
INFO Generated: archives/2026/index.html
INFO Generated: index.html
INFO Generated: css/style.css
INFO Generated: js/script.js
INFO Generated: fancybox/jquery.fancybox.min.js
INFO Generated: archives/2026/06/index.html
INFO Generated: fancybox/jquery.fancybox.min.css
INFO Generated: js/jquery-3.6.4.min.js
INFO Generated: css/images/banner.jpg
INFO Generated: 2026/06/24/hello-world/index.html
INFO 11 files generated in 172 ms
INFO Deploying: git
INFO Clearing .deploy_git folder...
INFO Copying files from public folder...
INFO Copying files from extend dirs...
warning: in the working copy of '2026/06/24/hello-world/index.html', LF will be replaced by CRLF the next time Git touches it
warning: in the working copy of 'archives/2026/06/index.html', LF will be replaced by CRLF the next time Git touches it
warning: in the working copy of 'archives/2026/index.html', LF will be replaced by CRLF the next time Git touches it
warning: in the working copy of 'archives/index.html', LF will be replaced by CRLF the next time Git touches it
warning: in the working copy of 'css/style.css', LF will be replaced by CRLF the next time Git touches it
warning: in the working copy of 'fancybox/jquery.fancybox.min.js', LF will be replaced by CRLF the next time Git touches it
warning: in the working copy of 'index.html', LF will be replaced by CRLF the next time Git touches it
warning: in the working copy of 'js/jquery-3.6.4.min.js', LF will be replaced by CRLF the next time Git touches it
warning: in the working copy of 'js/script.js', LF will be replaced by CRLF the next time Git touches it
[master 5005ddb] Site updated: 2026-06-24 15:45:25
2 files changed, 2 insertions(+), 2 deletions(-)
Enumerating objects: 51, done.
Counting objects: 100% (51/51), done.
Delta compression using up to 32 threads
Compressing objects: 100% (31/31), done.
Writing objects: 100% (51/51), 279.87 KiB | 890.00 KiB/s, done.
Total 51 (delta 10), reused 0 (delta 0), pack-reused 0 (from 0)
remote: Resolving deltas: 100% (10/10), done.
To github.com:AskemiWang/AskemiWang.github.io.git
* [new branch] HEAD -> main
branch 'master' set up to track 'git@github.com:AskemiWang/AskemiWang.github.io.git/main'.
INFO Deploy done: git

3.7 通过网页访问

访问:

1
https://yourname.github.io

比如:

myBlog

3.8 Hexo常用命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 初始化
hexo init [folder] # 初始化博客

# 开发
hexo new [layout] <title> # 新建文章
hexo server # 启动本地服务器
hexo generate # 生成静态文件
hexo clean # 清理缓存

# 部署
hexo deploy # 部署到服务器

# 快捷组合
hexo s # 启动服务器
hexo g # 生成文件
hexo d # 部署
hexo g -d # 生成并部署
hexo clean && hexo g -d # 清理、生成并部署

# 帮助
hexo help # 查看所有命令
hexo <command> --help # 查看具体命令帮助

4. 绑定域名

4.1 购买域名

GitHub使用Git Pages服务实现了通过访问用户站点网页来访问博客.但是名字太长了也不好记, 可以通过更换域名来解决这个问题.

在阿里云上购买域名, 并在控制台里面查询

1
https://home.console.aliyun.com/

阿里云真是扫码了, 开了代理后, 会自动跳到alibaba, 然而阿里巴巴没法用阿里云(国内)的邮箱登录.

4.2 添加解析

4.2.1 原理简述

下面解析域名, 也就是把我们的域名和IP关联起来, 一种可行的方案是使用CNAME的方式, 直接把我们的个人域名(xxx.cn)和主机记录(www)和刚刚的仓库域名yourname.github.io绑定, 但是这种方式下, 我们的个人域名访问只能是(www.xxx.cn)而不能访问xxx.cn.

所以我们使用A记录类型, 这种类型是绑定GitHub Pages的IPV4地址.

实际上的访问流程是这样工作的:

PersonalDomainDNSGitHub PagesDNSHostCertainRepo

我们把域名和GitHub Pages绑定, 这样访问的时候会直接访问到GitHub Pages的IP地址, 那么如何从GitHub Pages的IP确定到我们个人的仓库呢?

当用户访问自定义域名时,DNS 解析会将请求指向 GitHub Pages 的共享 IP 地址,随后浏览器在 HTTP 请求头中携带该域名信息;GitHub 服务器接收到请求后,会读取仓库根目录下的 CNAME 文件来验证域名归属并匹配对应的仓库内容,最终在同一共享 IP 上直接返回该仓库生成的静态页面。

4.2.2 具体解析步骤

在域名管理处添加新的记录

  1. 添加记录使根域名能直接访问:

  2. 万维网(www)访问

    • 记录类型: CNAME

    • 主机记录: www

    • 记录值: 仓库的实际域名

      1
      根域名

然后在我们的blog文件夹的source路径下新建CNAME文件(无后缀), 并添加我们的域名

1
2
3
4
5
6
7
test@ѩ▒ MINGW64 /e/myBlog
$ echo -e "wangyier.top" > source/CNAME

test@ѩ▒ MINGW64 /e/myBlog
$ cat source/CNAME
wangyier.top
www.wangyier.top

4.3 重新部署

1
hexo clean && hexo g -d

4.4 GitHub Pages验证域名所有权(域名绑定旧仓库问题)

前面讲过, 我们的DNS请求是到GitHub Pages上的, 还需要一步是通过CNAME, 建立一个GitHub Pages到我们仓库的映射.

如果我们在GitHub上其他仓库中绑定了域名, 访问我们自己的域名时, 会提示GitHub Pages的404, 这说明我们从域名到GitHub Pages成功了, 但是从GitHub Pages到我们的仓库没成功. 这个时候需要我们验证这个域名是自己的.

  1. 点击头像
  2. 选择Settings
  3. 左边侧边栏找到Pages
  4. 点击 Add a domain, 添加我们的根域名
  5. 会看到一段提示,要求你添加一条 DNS TXT 记录来验证所有权。页面会显示一个 TXT record 和 TXT record value
  6. 在购买域名的网站, 添加新的记录:
    • 记录类型: TXT
    • 主机记录: TXT record
    • 记录值: TXT record value
  7. 添加完后在GitHub上点击verify

此时我们需要在仓库的settings -> pages ->Custom domain这里, 填入我们自己的根域名xxx.cn

4.5 注意: 绑定域名后要额外修改配置

前面我们将_config.yml中的URL绑定到了我们的github域名上, 现在需要修改至我们的主域名.

5. Hexo全局配置

5.1 Hexo的_config.yaml配置

_config.yml文件是Hexo的全局配置文件.

配置项如下:

  • Site: 定义博客的全局“名片”信息。这些内容会被注入到网页的 HTML 头部(<title>, <meta> 标签),直接影响浏览器标签页的显示和搜索引擎的抓取(SEO)。

    • title: 网站主标题(显示在浏览器标签页和网站页头)。
    • subtitle: 网站副标题。
    • description: 网站描述(用于 <meta name="description">,搜索引擎摘要主要抓取这里)。
    • keywords: 网站关键词(用于 <meta name="keywords">,多个词用逗号隔开)。
    • author: 默认作者名称(当文章 Front-matter 中未指定作者时,使用此名称)。
    • language: 网站语言(如 en, zh-CN。修改此项可让主题界面的按钮、提示语切换为对应语言)。
    • timezone: 网站时区(如 Asia/Shanghai。影响文章发布时间的显示,留空则使用系统时区)。
  • URL: 控制网站的根地址以及文章永久链接(Permalink)的生成格式。

    • url: 网站的完整根网址。(注意:绑定自定义域名后,这里必须改为个人的域名比如 https://wangyier.top,否则图片等资源的绝对路径会出错)

    • root: 网站的根目录(默认为 /。如果部署在子目录下如 /blog/,需改为 /blog/)。

    • permalink: 文章的 URL 格式, 也就是你生成某个文章时的那个链接格式。

      比如我新建一个文章叫temp.md,那么这个时候他自动生成的地址就是http://yoursite.com/2018/09/05/temp。当前 :year/:month/:day/:title/ 表示生成类似 2026/06/25/hello-world/ 的链接,非常利于 SEO。

    • permalink_defaults: 当 permalink 中的变量为空时,使用的默认值。

    • pretty_urls: URL 美化设置。

      • trailing_index: 设为 false 可隐藏 URL 末尾的 index.html
      • trailing_html: 设为 false 可隐藏 URL 末尾的 .html 后缀。
  • Directory: 定义 Hexo 项目内部各个功能文件夹的物理存放路径。通常保持默认即可。

    • source_dir: 源文件目录(存放 Markdown 文章、图片等,默认 source)。
    • public_dir: 生成的静态网站输出目录(默认 public,部署时就是把这个文件夹推送到 GitHub)。
    • tag_dir / archive_dir / category_dir: 标签页、归档页、分类页的生成目录名。
    • code_dir: 存放包含的代码片段的目录。
    • i18n_dir: 多语言模板目录。
    • skip_render: 指定不需要被 Hexo 渲染、直接原样复制到 public 目录的文件或文件夹。
  • Writing: 控制新建文章的默认行为、外部链接处理逻辑以及代码高亮渲染引擎。

    • new_post_name: 新建文章时的文件名格式(默认 :title.md)。
    • default_layout: 默认布局(post 文章, page 独立页面, draft 草稿)。
    • titlecase: 是否将标题自动转换为首字母大写(Titlecase)。
    • external_link: 外部链接设置。
      • enable: 是否在新标签页打开外部链接。
      • field: 应用范围(site 全站, post 仅文章)。
    • filename_case: 文件名大小写转换(0: 不转换, 1: 全小写, 2: 全大写)。
    • render_drafts: 是否渲染草稿(设为 true 则草稿也会生成静态页面)。
    • post_asset_folder: 是否开启“文章资源文件夹”(开启后,新建文章会自动创建一个同名文件夹,方便管理文章内的图片)。
    • relative_link: 是否使用相对链接(通常配合 CDN 或子目录部署使用,默认 false)。
    • future: 是否显示未来日期的文章(设为 true 则即使文章日期是明天,也会显示)。
    • syntax_highlighter / highlight / prismjs: 代码高亮引擎及其详细配置(控制代码块是否显示行号、是否自动识别语言、缩进替换等)。
  • Home page setting: 专门控制博客首页(Index)的生成和展示逻辑

    • path: 首页的 URL 路径(默认为空,即根目录 /)。
    • per_page: 首页每页显示的文章数量(设为 0 则关闭分页,显示所有文章)。
    • order_by: 文章排序方式(-date 表示按日期倒序,即最新的文章排在最前面)。
  • Category & Tag: 设置文章分类和标签的默认行为及 URL 映射。

    • default_category: 当文章未指定分类时,自动归入的默认分类名(默认 uncategorized 未分类)。
    • category_map / tag_map: 分类/标签的别名映射字典。用于将中文分类名映射为英文 URL 路径(例如将 前端 映射为 frontend,使 URL 变成 /categories/frontend/
  • Metadata elements: 控制网页 <head> 中自动生成的 meta 标签。

    • meta_generator: 是否在网页头部生成 <meta name="generator" content="Hexo">(用于标识网站是用 Hexo 生成的,设为 false 可隐藏)。
  • Date / Time format: 自定义文章日期和时间的显示格式(基于 Moment.js 语法)。

    • date_format: 日期格式(如 YYYY-MM-DD)。
    • time_format: 时间格式(如 HH:mm:ss)。
    • updated_option: 当文章 Front-matter 中没有明确写 updated 字段时,如何获取更新时间(mtime 表示使用文件的最后修改时间)。
  • Pagination: 控制全站(如归档页、分类页、标签页)的分页行为。

    • per_page: 全局每页显示的文章数(如果首页设置了 per_page,则首页优先使用首页的设置)。
    • pagination_dir: 分页的 URL 目录名(默认 page,生成的 URL 类似 /page/2/)。
  • Include / Exclude file(s): 精细控制 source 目录下的文件在生成时是否被处理。

    • include: 强制包含的文件(即使它们被 ignore 规则排除了)。
    • exclude: 排除的文件(不会被复制到 public 目录)。
    • ignore: 忽略的文件/目录(Hexo 完全不处理它们)。
  • Extensions: 配置博客使用的主题和插件。

    • theme: 当前使用的主题名称。当前为 Hexo 官方默认的 landscape 主题(如果要换主题,只需修改这里并安装对应主题即可)。
  • Deployment: 配置 hexo deploy 命令的行为,定义如何将生成的静态文件推送到远程服务器。

    • type: 部署类型。当前为 git(需要安装 hexo-deployer-git 插件)。
    • repo: 目标仓库地址。当前使用的是 SSH 协议 (git@github.com:...),这比 HTTPS 更稳定且免密。
    • branch: 推送的目标分支。当前为 main(对应 GitHub 仓库的默认分支)。

5.2 category_maptag_map

Category和Tag需要额外注意:

Category是文章分类, 比如可以分为: 学习, 生活, 娱乐 …

Tag是文章具体标签, 比如Python, Hash, C++, Transformer…

category_map是做一个映射. 可以实现大小写统一, 缩写统一, …

  1. 大小写统一: 比如配置Tech: tech, 这样文章实际的category无论是大写还是小写, 都会映射成.../tech

  2. 统一缩写, 比如JS: JavaScript,JavaScript: javascript

  3. 特殊符号美化, 像GitHub Pages这种带空格的URL会转义成%20,非常不美观, 所以可以映射出一个不带空格的GitHub Pages: GitHub-Pages

    1
    2
    3
    4
    category_map:
    Tech: tech
    Life: life
    Entertaiment: entertaiment
    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
    tag_map:
    #Hexo
    HEXO: hexo
    Hexo: hexo
    # LLM
    LLM: llm
    Large Language Model: llm
    large language model: llm
    LargeLanguageModel: llm
    # ML
    ML: ml
    Machine Learning: ml
    machine learning: ml
    MachineLearning: ml
    # DL
    DL: dl
    Deep Learning: dl
    deep learning: dl
    DeepLearning: dl
    # AI
    AI: ai
    Artificial Intelligence: ai
    artificial intelligence: ai
    ArtificialIntelligence: ai
    # transformer & agent
    Transformer: transformer
    Agent: agent
    # programming languages
    Python: python
    C: c
    C++: cpp
    c++: cpp
    JavaScript: javascript
    JS: javascript

    # computer science
    OS: os
    Operating System: os
    operating system: os
    OperatingSystem: os

6. Hexo 优化

6.1 安装NexT主题

在blog根目录下, 下载主题:

1
git clone https://github.com/next-theme/hexo-theme-next.git themes/next

修改站点文件中的_config.yaml中的主题为next主题

1
2
3
4
# Extensions
## Plugins: https://hexo.io/plugins/
## Themes: https://hexo.io/themes/
theme: next

6.2 修改新建文章时的模板

修改scaffolds/post.mdscaffolds/draft.md 如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
---
title: {{ title }}
date: {{ date }}
updated: {{ date }}
permalink:
top: 0
password:
comments:
copyright: true
tags:
categories:
keywords:
description:
---

6.3 Next主题

下面将对Next主题进行一系列设置, 对应的配置文件是:themes/next/_config.yaml

6.3.1 设置darkmode

修改主题配置文件themes/next/_config.yamldarkmode 部分, 现在默认是darkmode, 不用改:

1
2
# Dark Mode
darkmode: true

6.3.2 修改网站图标

在 Hexo 的构建机制中,网站的根目录对应的是你本地项目中的 source 文件夹, 所以images文件夹的位置实际上是: myBlog/source/images, 刚开始这四个图标是不存在的, 我们需要自备一张图片, 通过:Favicon 生成网站修改成favicon文件, 或者直接在线制作, 我是直接在网站上在线制作的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
PS D:\chrome_download\favicon> ls .


目录: D:\chrome_download\favicon


Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 2026/6/25 16:12 32894 apple-touch-icon.png
-a---- 2026/6/25 16:12 11331 favicon-96x96.png
-a---- 2026/6/25 16:12 15086 favicon.ico
-a---- 2026/6/25 16:12 2275 favicon.svg
-a---- 2026/6/25 16:12 436 site.webmanifest
-a---- 2026/6/25 16:12 36994 web-app-manifest-192x192.png
-a---- 2026/6/25 16:12 228356 web-app-manifest-512x512.png

创建新site文件夹来存放这些图片:

1
mkdir -p /e/myBlog/source/images/site
1
cp /d/chrome_download/favicon/* /e/myBlog/source/images/site/

修改配置文件的favicon部分:

1
2
3
4
5
6
7
8
9
10
11
12
favicon:
small: /images/site/favicon.ico
medium: /images/site/favicon-96x96.png
apple_touch_icon: /images/site/apple-touch-icon.png
safari_pinned_tab: /images/site/favicon.svg
android_manifest: /images/site/site.webmanifest
# small: /images/favicon-16x16-next.png
# medium: /images/favicon-32x32-next.png
# apple_touch_icon: /images/apple-touch-icon-next.png
# safari_pinned_tab: /images/logo.svg
#android_manifest: /images/manifest.json
#ms_browserconfig: /images/browserconfig.xml

6.3.3 修改菜单栏

找到menu部分, 进行修改:

1
2
3
4
5
6
7
menu:
home: / || fa fa-home
archives: /archives/ || fa fa-archive
categories: /categories/ || fa fa-th
tags: /tags/ || fa fa-tags
about: /about/ || fa fa-user
Github: https://github.com/AskemiWang || fab fa-github

注意, 这里添加了categories, tags和 about页面的链接, 但是我们还没创建这三个页面.需要创建:

1
2
3
hexo new page categories
hexo new page tags
hexo new page about

然后需要分别对三个文件做初始化:

  1. 分类页面 (Categories): source/categories/index.md, 作用是告诉主题这里要渲染所有分类的列表。

    1
    2
    3
    4
    5
    ---
    title: Categories
    type: categories
    date: 2026-06-25 15:31:17
    ---
  2. 标签页面 (Tags): source/tags/index.md, 作用是告诉主题这里要渲染标签云或标签列表。

    1
    2
    3
    4
    5
    ---
    title: Tags
    type: tags
    date: 2026-06-25 15:31:14
    ---
  3. 关于页面 (About): source/about/index.md, 只需要正常的标题,然后在下方自由编写你的个人介绍(Markdown 正文)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    ---
    title: About Me
    date: 2026-06-25 15:31:34

    ---

    # Welcome to The Great Library

    Welcome to my personal digital space! This blog serves as my knowledge base, where I document my learning journey, technical explorations, and daily reflections.

    ## Focus Areas

    My studies and work primarily revolve around **Operating Systems** and **Large Language Models (LLMs)**. Consequently, the majority of the articles here are dedicated to these fields, along with related programming languages like C and Python.

    ## Connect with Me

    Feel free to reach out, collaborate, or just say hi:

    - 💻 **GitHub**: [AskemiWang](https://github.com/AskemiWang)
    - 📧 **Email**: [wye18280075427@gmail.com](mailto:wye18280075427@gmail.com)

6.3.4 边栏显示图像和社交信息

修改主题配置文件的 avatarsocial 字段, 注意要替换头像文件

先创建存放头像的文件夹, 并放入头像文件:

1
mkdir -p source/images/avatar
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
# Sidebar Avatar
avatar:
# Replace the default image and set the url here.
url: /images/avatar/avatar.png
# If true, the avatar will be displayed in circle.
rounded: false
# If true, the avatar will be rotated with the cursor.
rotated: true

# Posts / Categories / Tags in sidebar.
site_state: true

# Social Links
# Usage: `Key: permalink || icon`
# Key is the link label showing to end users.
# Value before `||` delimiter is the target permalink, value after `||` delimiter is the name of Font Awesome icon.
social:
GitHub: https://github.com/AskemiWang || fab fa-github
E-Mail: mailto:wye18280075427@https://mail.google.com/mail/?view=cm&fs=1&to=wye18280075427@gmail.com || fa fa-envelope
#Weibo: https://weibo.com/yourname || fab fa-weibo
#Twitter: https://twitter.com/yourname || fab fa-twitter
#FB Page: https://www.facebook.com/yourname || fab fa-facebook
#StackOverflow: https://stackoverflow.com/yourname || fab fa-stack-overflow
#YouTube: https://youtube.com/yourname || fab fa-youtube
#Instagram: https://instagram.com/yourname || fab fa-instagram
#Skype: skype:yourname?call|chat || fab fa-skype

social_icons:
enable: true
icons_only: false
transition: false

6.3.5 修改页脚时间和作者之间的图标

footer字段用来修改页脚部分的内容, since可以打开, 记录从多久开始建站; icon的animated也可以打开, 这样红心就会跳动了

1
2
3
4
5
6
7
8
9
10
11
footer:
# Specify the year when the site was setup. If not defined, current year will be used.
since: 2026
# Icon between year and copyright info.
icon:
# Icon name in Font Awesome. See: https://fontawesome.com/icons
name: fa fa-heart
# If you want to animate the icon, set it to true.
animated: true
# Change the color of icon, using Hex Code.
color: "#ff0000"

6.3.6 侧栏阅读进度提示

找到back2top字段, 修改scrollpercent为true, 这样会有阅读百分比提示:

1
2
3
4
5
6
back2top:
enable: true
# Back to top in sidebar.
sidebar: false
# Scroll percent label in b2t button.
scrollpercent: true

6.3.7 阅读进度条

找到reading_progress字段, 修改:

1
2
3
4
5
6
7
8
9
10
# Reading progress bar
reading_progress:
enable: true
# Available values: left | right
start_at: left
# Available values: top | bottom
position: bottom
reversed: false
color: "#37c6c0"
height: 3px

6.3.8 中英文自动加空格(不要使用!)

本来通过安装插件npm install hexo-pangu, 并且开启服务pangu选项, 可以增加中英文的空格, 更加美观.

1
pangu: true

实际上有个严重的问题: pangu在处理超链接的时候对中括号处理有问题, 所以一定要禁用:

1
pangu: false

6.3.9 开启“图片点击放大”功能

开启fancybox后,读者点击文章里的任何图片,都会弹出一个优雅的黑色遮罩层进行放大查看,体验极佳。

1
fancybox: true

6.3.10 开启“图片懒加载”(提升加载速度)功能

如果文章图片多,开启懒加载可以让图片只在滚动到屏幕可视区域时才加载,并带有淡入动画,既美观又省流量:

1
lazyload: true

6.3.11 开启“页面顶部加载进度条”(Pace)

在页面加载时,顶部会出现一条细细的进度条,消除用户的等待焦虑,非常有科技感。

1
2
3
4
pace:
enable: false
color: #37c6c0
theme: minimal

6.3.12 开启"本地搜索"功能

需要先安装插件:npm install hexo-generator-searchdb

然后设置字段:

1
2
local_search:
enable: true

6.3.13 开启“访客与阅读量统计”

1
2
busuanzi_count:
enable: true

6.3.14 开启“评论系统”(推荐 Utterances)

  1. 打开 Utterances 的官方配置页面:https://utteranc.es/

  2. 点击蓝色的 utterances app 链接(这会跳转到 GitHub 的授权页面)。

  3. 在 GitHub 页面中,选择 Only select repositories(仅选择特定仓库),然后在下拉菜单中勾选你的博客仓库.

  4. 点击绿色的 Install 按钮完成授权

  5. 在next配置文件中找到utterances字段并修改:

1
2
3
4
5
6
7
utterances:
enable: true
repo: uAskemiWang/AskemiWang.github.io # Github repository owner and name
# Available values: pathname | url | title | og:title
issue_term: pathname
# Available values: github-light | github-dark | preferred-color-scheme | github-dark-orange | icy-dark | dark-blue | photon-dark | boxy-light
theme: preferred-color-scheme

6.3.14 修改主题配置文件, 增加版权说明

先版本有自带的版权功能.下面是老方法:

修改custom_file_path字段:

1
2
3
4
5
6
7
8
9
10
11
12
custom_file_path:
head: source/_data/head.njk
#header: source/_data/header.njk
#sidebar: source/_data/sidebar.njk
#postMeta: source/_data/post-meta.njk
postBodyStart: source/_data/post-body-start.njk
postBodyEnd: source/_data/post-body-end.njk
footer: source/_data/footer.njk
#bodyEnd: source/_data/body-end.njk
#variable: source/_data/variables.styl
#mixin: source/_data/mixins.styl
style: source/_data/styles.styl

这里相当于从source/_data中读取了一些配置, 我们需要手动创建:

1
mkdir -p /e/myBlog/source/_data
1
2
3
4
5
touch /e/myBlog/source/_data/head.njk
touch /e/myBlog/source/_data/post-body-start.njk
touch /e/myBlog/source/_data/post-body-end.njk
touch /e/myBlog/source/_data/footer.njk
touch /e/myBlog/source/_data/styles.styl

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
/* ==========================================
美化 hexo-blog-encrypt 加密输入框
========================================== */

/* 1. 基础容器居中与间距 */
#hexo-blog-encrypt, .hbe-container {
text-align: center;
padding: 2em 0;
margin: 2em auto;
}

/* 2. 输入框核心样式 (适配亮色模式) */
.hbe-input-container {
position: relative;
width: 300px;
margin: 0 auto;

input#hbePass {
width: 100%;
padding: 10px 0;
font-size: 16px;
color: #333; /* 亮色模式文字 */
border: none;
border-bottom: 2px solid #ccc;
background: transparent;
outline: none;
transition: border-color 0.3s;

&:focus {
border-bottom-color: #37c6c0; /* 你的主题色 */
}
}

label {
position: absolute;
top: 10px;
left: 0;
font-size: 16px;
color: #999;
pointer-events: none;
transition: 0.3s ease all;
}

/* 输入内容或聚焦时,标签上浮 */
input:focus ~ label,
input:valid ~ label {
top: -20px;
font-size: 12px;
color: #37c6c0;
}

.bottom-line {
position: absolute;
bottom: 0;
left: 0;
width: 0;
height: 2px;
background: #37c6c0;
transition: width 0.3s;
}

input:focus ~ .bottom-line {
width: 100%;
}
}

/* 3. 完美适配暗黑模式 (Darkmode) */
.darkmode {
.hbe-input-container input#hbePass {
color: #fff; /* 暗黑模式文字变白 */
border-bottom-color: #555;
}
.hbe-input-container label {
color: #888;
}
/* 错误提示文字在暗黑模式下也要能看清 */
.hbe-incorrect-message, .hbe-error-message {
color: #ff7f50 !important;
}
}

6.3.15 鼠标点击爱心特效

  1. 创建love.js

    1
    mkdir -p /e/myBlog/source/js
  2. 写入love.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
    cat > source/js/love.js << 'EOF'
    !function(e, t, a) {
    function r() {
    for (var e = 0; e < s.length; e++) s[e].alpha <= 0 ? (t.body.removeChild(s[e].el), s.splice(e, 1)) : (s[e].y--, s[e].scale += .004, s[e].alpha -= .013, s[e].el.style.cssText = "left:" + s[e].x + "px;top:" + s[e].y + "px;opacity:" + s[e].alpha + ";transform:scale(" + s[e].scale + "," + s[e].scale + ") rotate(45deg);background:" + s[e].color + ";z-index:99999");
    requestAnimationFrame(r)
    }
    function n() {
    var t = "function" == typeof e.onclick && e.onclick;
    e.onclick = function(e) {
    t && t(), o(e)
    }
    }
    function o(e) {
    var a = t.createElement("div");
    a.className = "heart", s.push({
    el: a,
    x: e.clientX - 5,
    y: e.clientY - 5,
    scale: 1,
    alpha: 1,
    color: c()
    }), t.body.appendChild(a)
    }
    function i(e) {
    var a = t.createElement("style");
    a.type = "text/css";
    try {
    a.appendChild(t.createTextNode(e))
    } catch (t) {
    a.styleSheet.cssText = e
    }
    t.getElementsByTagName("head")[0].appendChild(a)
    }
    function c() {
    return "rgb(" + ~~(255 * Math.random()) + "," + ~~(255 * Math.random()) + "," + ~~(255 * Math.random()) + ")"
    }
    var s = [];
    e.requestAnimationFrame = e.requestAnimationFrame || e.webkitRequestAnimationFrame || e.mozRequestAnimationFrame || e.oRequestAnimationFrame || e.msRequestAnimationFrame || function(e) {
    setTimeout(e, 1e3 / 60)
    }, i(".heart{width: 10px;height: 10px;position: fixed;background: #f00;transform: rotate(45deg);-webkit-transform: rotate(45deg);-moz-transform: rotate(45deg);}.heart:after,.heart:before{content: '';width: inherit;height: inherit;background: inherit;border-radius: 50%;-webkit-border-radius: 50%;-moz-border-radius: 50%;position: fixed;}.heart:after{top: -5px;}.heart:before{left: -5px;}"), n(), r()
    }(window, document);
    EOF
  3. source/_data/head.njk中写入配置:

    1
    2
    3
    4
    cat > source/_data/head.njk << 'EOF'
    <!-- 页面点击小红心特效 -->
    <script type="text/javascript" src="/js/love.js"></script>
    EOF

6.3.16 文章加密功能

  1. 安装插件

    1
    npm install hexo-blog-encrypt --save
  2. 在Hexo配置(myBlog/_config.yml), 注意, 不是next主题配置, 末尾添加:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    # 文章加密配置 (hexo-blog-encrypt)
    encrypt:
    enable: true
    # 首页显示的摘要提示
    abstract: '🔒 这篇文章已被加密,请输入密码查看。'
    # 密码输入框的提示文字
    message: '请输入访问密码:'
    # 密码错误时的提示
    wrong_pass_message: '密码错误,请重新输入!'

    # 【关键修改】使用内置主题,不再使用 template 写 HTML
    # 可选值: default, blink, flip, shrink, surge, up, wave, xray
    # 推荐使用 default 或 blink
    theme: default

    # 【消除警告】提高密码派生函数的迭代次数,增强防暴力破解能力
    kdf:
    iterations: 600000

6.3.17 网站运行时间统计

source\_data\footer.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
25
26
27
28
29
30
31
32
33
<!-- 网站运行时间计数 -->
<span id="runtime_span" style="margin-left: 10px; font-size: 14px;">
🕒 本站已运行 <span id="runtime_days"></span> 天 <span id="runtime_hours"></span> 小时 <span id="runtime_mins"></span> 分 <span id="runtime_secs"></span> 秒
</span>

<script>
function show_runtime() {
window.setTimeout("show_runtime()", 1000);

// 【⚠️ 重要】请修改为你真实的建站日期 (年/月/日 时:分:秒)
// 注意:月份是从 1 开始的,直接填数字即可
var BirthDay = new Date("2026/06/24 00:00:00");

var today = new Date();
var timeold = (today.getTime() - BirthDay.getTime());
var msPerDay = 24 * 60 * 60 * 1000;
var e_daysold = timeold / msPerDay;

var daysold = Math.floor(e_daysold);
var e_hrsold = (e_daysold - daysold) * 24;
var hrsold = Math.floor(e_hrsold);
var e_minsold = (e_hrsold - hrsold) * 60;
var minsold = Math.floor((e_hrsold - hrsold) * 60);
var seconds = Math.floor((e_minsold - minsold) * 60);

// 将计算结果填入对应的 span 标签中
document.getElementById("runtime_days").innerHTML = daysold;
document.getElementById("runtime_hours").innerHTML = hrsold;
document.getElementById("runtime_mins").innerHTML = minsold;
document.getElementById("runtime_secs").innerHTML = seconds;
}
show_runtime();
</script>

6.3.17 关闭next主题的标题自动排序

假如我们平时写markdown文档的时候, 就有良好的分级习惯, 就不需要next主题来帮我们排序, 不然会出现重复给标题分级的情况, 将toc字段的number关闭:

1
2
3
4
5
6
7
8
9
10
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: false
# Maximum heading depth of generated toc.
max_depth: 6

6.3.18 文章顶部显示tag

next主题中, 文章元数据中默认只显示category不显示tag, 所以我们要在post_metazpost_meta字段中添加tags:

1
2
3
4
5
6
7
8
9
# Post meta display settings
post_meta:
item_text: true
created_at: true
updated_at:
enable: true
another_day: true
categories: true
tags: true

6.3.19 支持Latex渲染

原生的Hexo不支持latex, 需要安装插件解决:

  1. 确保没有安装hexo-math(已过时):

    1
    2
    3
    4
    npm uninstall hexo-renderer-marked hexo-renderer-kramed hexo-renderer-pandoc --save
    npm uninstall hexo-math mathjax --save
    npm uninstall hexo-pangu --save
    npm install hexo-renderer-markdown-it markdown-it-mathjax3 --save
  2. 在next主题中配置文件中, 修改math字段:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    math:
    # Default (false) will load mathjax / katex script on demand.
    # That is it only render those page which has `mathjax: true` in front-matter.
    # If you set it to true, it will load mathjax / katex script EVERY PAGE.
    every_page: true

    mathjax:
    enable: true
    # Available values: none | ams | all
    tags: none

    katex:
    enable: false
    # See: https://github.com/KaTeX/KaTeX/tree/master/contrib/copy-tex
    copy_tex: false
  3. 更换一个更加友好的渲染器

    1
    npm install hexo-renderer-markdown-it markdown-it-mathjax3 --save
  4. 修改Hexo的配置文件, 注意不是next的, 在文件末尾添加以下配置,告诉 Hexo 使用新的数学公式插件:

1
2
3
4
5
6
# Markdown 渲染配置
markdown:
render:
html: true # 允许在 Markdown 中使用 HTML 标签
plugins:
- markdown-it-mathjax3 # 启用 MathJax3 插件,完美保护 $ 和 $$ 不被破坏

7. Hexo 发布新文章

7.1 Front-matter字段

使用hexo new filename即可在source/_posts下面创建对应的文章. 文章初始只有下面的一段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
---
title: test
top: 0
copyright: true
date: 2026-06-26 13:02:12
updated: 2026-06-26 13:02:12
permalink:
password:
comments:
tags:
categories:
keywords:
description:
---

被两个 --- 包裹的区域,在 Hexo 中被称为 Front-matter(文章元数据)。它使用 YAML 语法编写,用于告诉 Hexo 和 Next 主题该如何处理、展示这篇特定的文章。

对于其他的md文件, 我们可以手动加上开头这一段来让它变成可以被hexo渲染的博客文件.

下面说明这些字段的功能和用法:

基础字段:

字段 作用 示例
title 文章标题
显示在浏览器标签页、首页列表和文章页顶部。
title: Python 基础入门学习笔记
date 创建/发布时间
Hexo 根据这个时间对文章进行排序
date: 2026-06-26 13:02:12
(保持默认即可)
updated 最后修改时间
如果你以后修改了文章,填上修改时间,Next 主题会在文章顶部显示“更新于 XXXX”。
updated: 2026-06-27 10:00:00
(不填则默认使用 date)
categories 文章分类
决定文章归属哪个专栏,会在侧边栏和分类页展示。
categories: [Tech]
tags 文章标签
提取文章的核心知识点,会在标签页展示。
tags: [Python, 编程基础]
description 文章摘要
1. 显示在首页文章列表中(代替自动截取的正文);
2. 作为网页的 <meta name="description">对 SEO 极其重要
description: 本文记录了 Python 环境搭建及基础语法的学习心得。

高级字段(按需使用,不用全填):

建议所有带特殊符号的文章名, 都使用一下permalink, 比如本文的名字是Build Blog with Hexo, 如果不改permalink, 文章的URL是:

1
https://wangyier.top/2026/06/26/Build%20Blog%20with%20Hexo/

如果把permalink手动指定为Buil-Blog-with-Hexo/, 那么URL会变成:

1
https://wangyier.top/2026/06/26/Buil-Blog-with-Hexo/

更加美观, 注意手动指定要加/

字段 作用 示例
top 文章置顶
数字越大,在首页排得越靠前。设为 0 或删掉该行表示不置顶。
top: 1 (置顶) / top: 0 (不置顶)
password 文章加密
配合你之前安装的 hexo-blog-encrypt 插件,填入密码后文章将被加密。
password: mysecret123 (不填或留空则公开)
copyright 版权声明开关
覆盖全局的 CC 协议设置。如果你想某篇文章不显示底部的版权声明,可设为 false
copyright: true (显示) / false (隐藏)
comments 评论区开关
覆盖全局的 Utterances 评论设置。如果是纯公告或草稿,可关闭评论。
comments: true (开启) / false (关闭)
permalink 自定义永久链接
覆盖全局的 URL 生成规则。如果你想让某篇文章的 URL 特别短或特殊,可以用这个。
permalink: python-basics/ (URL变为 .../python-basics/)
keywords 文章关键词
覆盖全局的 keywords,专门针对这篇文章的 SEO 优化。
keywords: [Python, 教程, 新手]

7.2 注意事项

  1. 冒号后面必须有且只有一个空格

  2. 列表(数组)的两种正确写法:

    • 写法 A(推荐,紧凑)tags: [Python, AI, 操作系统] (注意逗号后要有空格)

    • 写法 B(多行,适合标签多时)

      1
      2
      3
      4
      tags:
      - Python
      - AI
      - 操作系统

      注意:- 前面必须有两个空格的缩进,- 后面必须有一个空格!

  3. 特殊字符加引号:

    如果你的标题或描述里包含冒号 :、中括号 []、大括号 {} 等特殊符号,必须用单引号或双引号把整句话包起来。

    正确示例: title: 'Python 教程: 从入门到放弃'

    错误示例: title: Python 教程: 从入门到放弃, 因为(YAML 会把冒号当成键值对解析,导致报错)

8. 源码备份

事实上, 每次执行hexo d的时候, 只是推送了一部分md文件和生成好的前端文件到GitHub上, 这样做会有一个代码丢失的风险, 假如本地代码被破坏或丢失, 那么将不能恢复到以前的状态, 所以我们需要一个额外的仓库来备份我们的整个Hexo项目:

  1. 登录 GitHub,点击右上角 “+” -> “New repository”

  2. Repository name 填入:hexo-blog-source(或者你喜欢的名字)。

  3. Visibility 务必选择:Private (私有)

  4. 不要勾选 “Add a README file”(保持空仓库)。

  5. 点击 Create repository

  6. 在本地的blog根目录下添加或修改.gitignore文件, 保证文件中拥有以下内容:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    # Hexo 生成和部署的临时目录
    public/
    .deploy_git/
    db.json

    # Node.js 依赖包(体积巨大,随时可以通过 npm install 恢复)
    node_modules/

    # 系统生成的垃圾文件
    .DS_Store
    Thumbs.db
    *.log
  7. 将源码推送到私有仓库, 在myBlog文件夹中使用以下命令, 注意, 由于我们next主题是用git下载的, 所以要删掉他的.git文件避免冲突:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    # 1. 核心操作:删除 Next 主题内部的 .git 目录(解决嵌套仓库问题)
    rm -rf themes/next/.git

    # 2. 初始化本地 Git 仓库(如果之前已经 init 过,提示已存在则忽略)
    git init

    # 3. 将所有源码添加到暂存区
    git add .

    # 4. 提交到本地仓库
    git commit -m "Initial commit: Backup hexo source code"

    # 5. 关联你的 GitHub 私有仓库(使用你提供的地址)
    git remote add origin https://github.com/AskemiWang/hexo-blog-source.git

    # 6. 重命名主分支并推送到 GitHub
    git branch -M main
    git push -u origin main

9. 图片问题

文章中插入图片就两种方式, 利用图床或者插入本地的图片. 我个人对插入图片很有执念, 因为mermaid太难用了.

然而我个人不放心图床的方式, 有跑路的风险(, 决定用本地的图片.

这样有一个问题, 如果图片数量过多, 仓库的体积就会很大, 后面甚至不能push, 所以我决定采用压缩图片的方法. 流程上:

1
2
3
4
5
6
7
用一个log文件记录图片是否被压缩过了
->
没压缩就压缩,压缩过了就不管
->
备份文件到GitHub私密仓库
->
部署到公开仓库

Pillow 是 Python 最流行的图像处理库,用于压缩图片。

1
pip install pillow

默认把所有文章的图片放在:

1
source/images/article_images

我们的文章在:

1
source/_post

所以图片的相对路径是:

1
../images/article_images

下面是测试文件, 原大小为653KB:

测试图片

python处理脚本(博客根目录下的compress_images.py):

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
import os
from pathlib import Path
from PIL import Image

# ==================== 配置区 ====================
BLOG_DIR = Path(__file__).parent
SOURCE_DIR = BLOG_DIR / "source"
LOG_FILE = BLOG_DIR / ".compressed_log"

# 压缩参数
JPEG_QUALITY = 82 # JPEG 质量 (1-100)
PNG_COMPRESS_LEVEL = 8 # PNG 压缩级别 (1-9)
SUPPORTED_FORMATS = {".jpg", ".jpeg", ".png"}

# ==================== 核心逻辑 ====================
def load_log():
if LOG_FILE.exists():
return set(LOG_FILE.read_text(encoding="utf-8").splitlines())
return set()

def save_log(log_set):
LOG_FILE.write_text("\n".join(sorted(log_set)), encoding="utf-8")

def compress_image(filepath, log_set):
rel_path = str(filepath.relative_to(BLOG_DIR))

# 1. 检查是否已压缩过
if rel_path in log_set:
return "skip", 0, 0

original_size = filepath.stat().st_size
# 使用临时文件,防止压缩后体积反而变大时破坏原图
temp_path = filepath.with_name(filepath.name + ".tmp")

try:
with Image.open(filepath) as img:
suffix = filepath.suffix.lower()

if suffix in {".jpg", ".jpeg"}:
if img.mode in ("RGBA", "P"):
img = img.convert("RGB")
img.save(temp_path, "JPEG", quality=JPEG_QUALITY, optimize=True)

elif suffix == ".png":
if img.mode == "RGBA":
img.save(temp_path, "PNG", optimize=True, compress_level=PNG_COMPRESS_LEVEL)
else:
img = img.convert("P", palette=Image.ADAPTIVE, colors=256)
img.save(temp_path, "PNG", optimize=True, compress_level=PNG_COMPRESS_LEVEL)

new_size = temp_path.stat().st_size

# 2. 判断压缩效果
if new_size < original_size:
os.replace(temp_path, filepath) # 替换原文件
return "success", original_size, new_size
else:
os.remove(temp_path) # 删除临时文件,保留原图
return "skip_opt", original_size, original_size

except Exception as e:
if temp_path.exists():
os.remove(temp_path)
return "error", original_size, 0

def main():
print("=" * 55)
print(" Hexo Image Compressor")
print("=" * 55)

# 扫描图片
images = []
for ext in SUPPORTED_FORMATS:
images.extend(SOURCE_DIR.rglob(f"*{ext}"))

log_set = load_log()
success_count = 0
skip_count = 0

print(f"\nScanning {len(images)} images in 'source/'...\n")

# 处理图片
for img_path in images:
rel_path = str(img_path.relative_to(BLOG_DIR))
status, orig, new = compress_image(img_path, log_set)

if status == "success":
ratio = (1 - new / orig) * 100
print(f" [OK] {rel_path} ({orig/1024:.1f}KB -> {new/1024:.1f}KB, -{ratio:.1f}%)")
log_set.add(rel_path)
success_count += 1
elif status == "skip":
skip_count += 1
elif status == "skip_opt":
print(f" [SKIP] {rel_path} (Already optimized)")
log_set.add(rel_path)
skip_count += 1
elif status == "error":
print(f" [ERR] {rel_path} (Failed)")

# 保存日志
if success_count > 0 or skip_count > 0:
save_log(log_set)

print(f"\nSummary: {success_count} compressed, {skip_count} skipped.")
print("=" * 55)

if __name__ == "__main__":
main()

执行完之后的输出:

1
2
3
4
5
6
7
8
Scanning 6 images in 'source/'...

[OK] source\images\article_images\test.png (653.6KB -> 628.2KB, -3.9%)
[OK] source\images\avatar\avatar.png (21.3KB -> 13.9KB, -34.8%)
[OK] source\images\site\apple-touch-icon.png (32.1KB -> 9.7KB, -69.7%)
[OK] source\images\site\favicon-96x96.png (11.1KB -> 4.6KB, -58.4%)
[OK] source\images\site\web-app-manifest-192x192.png (36.1KB -> 10.7KB, -70.4%)
[OK] source\images\site\web-app-manifest-512x512.png (223.0KB -> 40.2KB, -82.0%)

.compressed_log文件中的记录:

1
2
3
4
5
6
source\images\article_images\test.png
source\images\avatar\avatar.png
source\images\site\apple-touch-icon.png
source\images\site\favicon-96x96.png
source\images\site\web-app-manifest-192x192.png
source\images\site\web-app-manifest-512x512.png

10. 博客更新流程

  1. 创建/编辑博客

    1
    hexo new filename

    编辑…

  2. 压缩图片

    1
    python compress_images.py
  3. 备份源码

    1
    git add . && git commit -m "update" && git push
  4. 发布博客

    1
    hexo clean && hexo g -d

11. 更换电脑后如何更新博客

以下内容未经测试, 实际方法可能有偏差:

  1. 克隆私有源码仓库:

    1
    git clone https://github.com/AskemiWang/hexo-blog-source.git myBlog
  2. 安装 Node.js 依赖(恢复插件和主题依赖), 由于Next 主题文件是直接存在 themes/next 里的,克隆时已经一起下载下来了,所以不需要重新下载主题。

    1
    2
    cd myBlog
    npm install
  3. 本地验证

    1
    hexo clean && hexo g && hexo s
  4. Git配置

    1
    2
    git config --global user.name "AskemiWang"
    git config --global user.email "wye18280075427@gmail.com"
  5. 恢复日常工作流

    1
    python compress_images.py
    1
    git add . && git commit -m "update" && git push
    1
    hexo clean && hexo g -d

12. 一键更新

注意, 没开代理的情况下git push可能会失败, 后面的hexo命令也不会执行

1
cd /e/myBlog && python compress_images.py && git add . && git commit -m "update" && git push && hexo clean && hexo g -d

本文链接:https://wangyier.top/Build-Blog-with-Hexo/

版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 The Great Library