Hexo 钩子生命周期
Hexo 提供了两种钩子机制来让开发者介入博客的构建流程:事件(Events) 和过滤器(Filters)。本文将按照执行顺序,逐一讲解所有钩子,并注明哪些是特定命令所独有的。
两种钩子机制概览
事件(Events)
Hexo 继承了 Node.js 的 EventEmitter,你可以通过 hexo.on() 方法监听事件,通过 hexo.emit() 方法触发事件。
// 监听事件示例
hexo.on('generateBefore', function () {
console.log('生成即将开始...');
});
过滤器(Filters)
过滤器用于修改特定的数据,Hexo 将数据按顺序传递给过滤器,过滤器逐个修改数据。注册方式为:
hexo.extend.filter.register('过滤器名称', function (data) {
// 修改 data
return data;
}, priority);
priority 值越低,执行越早,默认为 10。
钩子执行顺序
第一阶段:初始化
| 顺序 | 钩子类型 | 名称 | 触发时机 |
|---|---|---|---|
| 1 | 事件 | ready |
Hexo 初始化完成后触发。 |
| 2 | 过滤器 | after_init |
Hexo 初始化完成后执行(紧随 hexo.init 完成之后)。 |
这是所有命令都会经过的入口阶段。ready 事件和 after_init 过滤器在功能上类似,都在初始化完成后触发。
第二阶段:处理源文件(Processing)
这个阶段 Hexo 会扫描并处理 source 文件夹中的源文件(如 Markdown 文章)。
| 顺序 | 钩子类型 | 名称 | 触发时机 |
|---|---|---|---|
| 3 | 事件 | processBefore |
开始处理原始文件前触发。此事件返回一个路径,代表 Box(源文件目录)的根目录。 |
| 4 | 过滤器 | post_permalink |
用于确定文章的永久链接(Permalink)。 |
| 5 | 过滤器 | before_post_render |
在每篇文章开始渲染前执行。 |
| 6 | (内置渲染) | Markdown/渲染器渲染 | 使用 Markdown 或其他渲染器进行渲染(取决于扩展名)。 |
| 7 | (内置渲染) | Nunjucks 渲染 | 使用 Nunjucks 模板引擎渲染。 |
| 8 | 过滤器 | after_post_render |
在每篇文章渲染完成后执行。 |
| 9 | 事件 | processAfter |
所有原始文件处理完成后触发。此事件返回一个路径,代表 Box 的根目录。 |
注意:before_post_render 和 after_post_render 是针对每篇文章逐个执行的,而不是只执行一次。
第三阶段:生成静态文件(Generation)
这个阶段 Hexo 的生成器(Generator)会创建静态 HTML 文件。
| 顺序 | 钩子类型 | 名称 | 触发时机 |
|---|---|---|---|
| 10 | 事件 | generateBefore |
在生成器开始工作前触发。 |
| 11 | 过滤器 | before_generate |
在生成过程开始前执行。 |
| 12 | 过滤器 | template_locals |
修改模板的局部变量(在生成路由后、渲染模板前)。 |
| 13 | (内置) | 模板渲染 | Hexo 使用模板引擎渲染生成最终的 HTML 页面。 |
| 14 | 过滤器 | after_render |
在每次渲染完成后执行。这是一个过滤器家族,可以根据输出格式细分为多种类型: • after_render:html — 处理 HTML 文件• after_render:css — 处理 CSS 文件• after_render:js — 处理 JavaScript 文件• after_render:xml — 处理 XML 文件(如 Sitemap)注册时需指定具体格式,如 hexo.extend.filter.register('after_render:html', fn)。 |
| 15 | 事件 | generateAfter |
在生成器工作完成后触发。 |
| 16 | 过滤器 | after_generate |
在生成完成后执行。 |
说明:
hexo generate和hexo server在启动阶段都会完整经历上述第 10-16 步。after_render过滤器会在每一次渲染完成后执行,不仅仅是在最终页面生成时。这意味着它也可能在渲染文章摘要、主题的局部模板(如侧边栏)等场景下被触发。如果你的逻辑只需在完整页面上生效,需要通过data参数中的上下文信息(如data.layout)进行判断。
第四阶段:特有行为
hexo generate
| 顺序 | 钩子类型 | 名称 | 触发时机 | 适用命令 |
|---|---|---|---|---|
| 17 | 过滤器 | before_exit |
在 Hexo 即将退出时执行(紧随 hexo.exit 被调用之后)。 |
hexo generate、hexo server(手动停止时) |
| 18 | 事件 | exit |
在 Hexo 进程退出前触发。 | hexo generate、hexo server(手动停止时) |
hexo generate 在生成完静态文件后会立即退出,因此第 17-18 步会在生成完成后马上触发。
hexo server
| 顺序 | 钩子类型 | 名称 | 触发时机 | 适用命令 |
|---|---|---|---|---|
| 17 | 过滤器 | server_middleware |
添加中间件到服务器。此钩子由 hexo-server 提供,非 Hexo 核心,仅在安装 hexo-server 时运行。 |
hexo server 独有 |
| 18 | (内置) | 启动服务器 | Hexo 启动 Web 服务器,在控制台输出本地访问地址(如 http://localhost:4000),并持续监听文件变化。 |
hexo server 独有 |
| 19 | 过滤器 | before_exit |
在 Hexo 即将退出时执行(手动停止服务器时,如按 Ctrl+C)。 |
hexo server 独有(手动停止时) |
| 20 | 事件 | exit |
在 Hexo 进程退出前触发(手动停止服务器时)。 | hexo server 独有(手动停止时) |
关键区别:
hexo generate:生成后直接退出,触发before_exit/exithexo server:生成后保持运行,before_exit/exit在手动停止时才触发server_middleware是hexo server独有的钩子
其他独立钩子
以下钩子不属于上述主流程,但在特定场景下会被触发:
| 钩子类型 | 名称 | 触发时机 | 适用命令 |
|---|---|---|---|
| 事件 | new |
通过 hexo new 命令成功创建新文章后触发。返回 post.path(完整路径)和 post.content(内容)。 |
hexo new 独有 |
| 过滤器 | new_post_path |
创建新文章时,用于决定新文章的保存路径。 | hexo new 独有 |
| 事件 | deployBefore |
在部署开始前触发。 | hexo deploy 独有 |
| 事件 | deployAfter |
在部署完成后触发。 | hexo deploy 独有 |
| 过滤器 | after_clean |
在 hexo clean 命令移除生成文件和缓存后执行。 |
hexo clean 独有 |
| 过滤器 | router |
在渲染路由之前修改路由数据。此过滤器可用于添加、删除或修改路由。 | 所有命令 |
钩子使用示例
监听事件
在 Hexo 根目录的 scripts/ 文件夹中创建脚本文件:
// scripts/my-hooks.js
hexo.on('ready', function () {
console.log('✅ Hexo 已就绪!');
});
hexo.on('generateBefore', function () {
console.log('⏳ 开始生成静态文件...');
});
hexo.on('generateAfter', function () {
console.log('✅ 生成完成!');
});
hexo.on('exit', function () {
console.log('👋 Hexo 进程即将退出');
});
注册过滤器
// scripts/my-filters.js
// 在文章渲染前将标题转为小写
hexo.extend.filter.register('before_post_render', function (data) {
data.title = data.title.toLowerCase();
return data;
}, 10);
// 在文章渲染后替换内容中的 @用户名 为 Twitter 链接
hexo.extend.filter.register('after_post_render', function (data) {
data.content = data.content.replace(
/@(\w+)/g,
'<a href="https://twitter.com/$1">@$1</a>'
);
return data;
}, 10);
// 在生成前执行自定义逻辑
hexo.extend.filter.register('before_generate', function () {
console.log('⏳ 生成前执行自定义逻辑...');
});
// 修改模板局部变量
hexo.extend.filter.register('template_locals', function (locals) {
locals.currentYear = new Date().getFullYear();
return locals;
});
// 压缩 HTML(使用 after_render:html)
const htmlMinifier = require('html-minifier');
hexo.extend.filter.register('after_render:html', function (str, data) {
// str: 渲染完成后的 HTML 字符串
// data: 包含页面相关信息的对象
// 注意:此过滤器会在每次渲染完成后执行,不仅限于完整页面
// 可以通过 data.layout 判断是否为完整页面
if (data.layout && data.layout !== 'false') {
return htmlMinifier.minify(str, {
removeComments: true,
collapseWhitespace: true,
minifyJS: true,
minifyCSS: true
});
}
return str;
}, 10);
过滤器优先级
优先级数字越小,执行越早:
// 优先执行(priority: 1)
hexo.extend.filter.register('after_post_render', function (data) {
// 最先执行
return data;
}, 1);
// 后执行(priority: 20)
hexo.extend.filter.register('after_post_render', function (data) {
// 最后执行
return data;
}, 20);
快速参考表
| 阶段 | 钩子名称 | 类型 | hexo g |
hexo s |
hexo d |
hexo new |
hexo clean |
|---|---|---|---|---|---|---|---|
| 初始化 | ready |
事件 | ✅ | ✅ | ✅ | ✅ | ✅ |
| 初始化 | after_init |
过滤器 | ✅ | ✅ | ✅ | ✅ | ✅ |
| 处理源文件 | processBefore |
事件 | ✅ | ✅ | ✅ | ✅ | ✅ |
| 处理源文件 | post_permalink |
过滤器 | ✅ | ✅ | ✅ | ✅ | ✅ |
| 处理源文件 | before_post_render |
过滤器 | ✅ | ✅ | ✅ | ✅ | ✅ |
| 处理源文件 | after_post_render |
过滤器 | ✅ | ✅ | ✅ | ✅ | ✅ |
| 处理源文件 | processAfter |
事件 | ✅ | ✅ | ✅ | ✅ | ✅ |
| 生成 | generateBefore |
事件 | ✅ | ✅ | ❌ | ❌ | ❌ |
| 生成 | before_generate |
过滤器 | ✅ | ✅ | ❌ | ❌ | ❌ |
| 生成 | template_locals |
过滤器 | ✅ | ✅ | ❌ | ❌ | ❌ |
| 生成 | after_render(及 :html 等变体) |
过滤器 | ✅ | ✅ | ❌ | ❌ | ❌ |
| 生成 | generateAfter |
事件 | ✅ | ✅ | ❌ | ❌ | ❌ |
| 生成 | after_generate |
过滤器 | ✅ | ✅ | ❌ | ❌ | ❌ |
| 生成 | router |
过滤器 | ✅ | ✅ | ❌ | ❌ | ❌ |
| 服务器 | server_middleware |
过滤器 | ❌ | ✅ 独有 | ❌ | ❌ | ❌ |
| 部署 | deployBefore |
事件 | ❌ | ❌ | ✅ 独有 | ❌ | ❌ |
| 部署 | deployAfter |
事件 | ❌ | ❌ | ✅ 独有 | ❌ | ❌ |
| 新建文章 | new |
事件 | ❌ | ❌ | ❌ | ✅ 独有 | ❌ |
| 新建文章 | new_post_path |
过滤器 | ❌ | ❌ | ❌ | ✅ 独有 | ❌ |
| 清理 | after_clean |
过滤器 | ❌ | ❌ | ❌ | ❌ | ✅ 独有 |
| 退出 | before_exit |
过滤器 | ✅ | ✅(手动停止) | ✅ | ✅ | ✅ |
| 退出 | exit |
事件 | ✅ | ✅(手动停止) | ✅ | ✅ | ✅ |
总结
- 事件(Events) 和 过滤器(Filters) 是 Hexo 提供的两种钩子机制,分别用于监听流程节点和修改数据。
- 核心流程:所有构建命令(
generate、server)都会经历初始化 → 处理源文件 → 生成静态文件三个阶段。 - 命令特有钩子:
hexo server独有server_middleware过滤器hexo deploy独有deployBefore/deployAfter事件hexo new独有new事件和new_post_path过滤器hexo clean独有after_clean过滤器
after_render过滤器家族:after_render本身是一个过滤器类型,可细分为after_render:html、after_render:css、after_render:js、after_render:xml等。注册时需指定具体格式,且该过滤器会在每次渲染完成后执行,不仅限于最终页面。- 退出时机:
hexo generate在生成完成后立即退出;hexo server保持运行,直到手动停止才触发退出钩子。