hexo 是如何利用 theme 下的文件生成 html 页面的?
hexo-generator 是如何利用插件的?hexo-generator 生成的结果是什么数据?
为什么我的 archives/index.html 会利用 theme/layout/archive.ejs 生成?
为什么 tags/index.html 没有生成,/tags/tag-a.html 却生成了,并且利用了 theme/tag.ejs 这个模板?
通过源码探索
我们执行 hexo generate
的时候,依赖了博客目录下 node_modules/hexo
里面的代码,那么直接从依赖里面开找。
从 hexo/index.js
开始
生成网站 html
文件需要我们执行 hexo generate
。所以直接从 generate
开始看,从 index.js
搜索到 _generate
方法如下:
1 | _generate(options = {}) { |
这段代码中,我们主要关注this._runGenerators()
和 this._routerRefresh
部分。
1 | _runGenerators() { |
我们在 _runGenerators
中可以看到,它将 generator
这个方法类型的变量进行执行,并将返回的结果整合到一起返回。
然后在 _routerRefresh
会对每一个 generatorResult
判断它的三个属性 path
, layout
和 data
。
我们可以得知,每一个 generator
执行后,返回的结果结构就会包含这三个字段。
接下来我们关注 createLoadThemeRoute()
方法,这个方法就是对应通过 theme 生成 html 的逻辑。
1 | const createLoadThemeRoute = function (generatorResult, locals, ctx) { |
我们可以看到,这里通过遍历 generatorResult
的 layout
字段,这个数组字段,从 theme/layout
文件夹下面匹配我们编写的模板文件。
同时,因为 for 循环中使用了 return
,所以会按照 generatorResult.layout
字段数组顺序,在 theme/layout
文件夹下找对应的模板文件,找到第一个就返回。
再看 hexo 中定义的 generater
我们知道创建 generator
的时候,需要在 index.js 中编写以下代码:
hexo.extend.generator.register('', fn);
所以我们直接去 hexo 的 extend 文件夹中寻找 generator 相关的定义:
1 | import Promise from "bluebird"; |
可以看到这里声明了我们使用的 register
方法,以及 GeneratorFunction
的定义。
而 GeneratorFunction
定义的返回值类型 BaseObj
正对应了我们上面发现的 generator
方法的返回值的三个字段:
1 | interface BaseObj { |
回到具体的 generator
上
我们可以看到 node_modules
中引入了相当多 hexo-generator-*
的依赖,就是这些依赖帮助我们控制如何生成对应的网页。
我们以 hexo-generator-tag
进行举例,首先在 index.js
中注册 generator
:
1 | /* global hexo */ |
然后在 lib 中提供 generator
的方法,并返回带有 path
, layout
和 data
的数据。
1 | // ...... |
总结生成器生成 html 的流程
首先 hexo 会去 node_modules 下面遍历 hexo-generator-*
的依赖,把这些 generator
都进行加载。
然后 generator
执行,返回带有 path
, layout
和 data
结构的数据。
其中 path
对应了生成文件的路径,layout
指定了渲染 theme 下的哪一个模板。
最后 hexo 通过 createLoadThemeRoute
方法将 generator
生成的数据和 theme
下的模板文件结合到一起,生成出最终的 html
文件。
解答开篇的问题
通过上面的代码分析,可以得知前两个比较 common 的问题的答案。
下面我们主要分析下剩下两个具体问题的答案:
为什么我的 archives/index.html 会利用 theme/layout/archive.ejs 生成?
解答这个问题需要查看
hexo-generator-achive
的代码:1
2
3
4
5
6
7
8
9
10
11
12
13
14// ......
function generate(path, posts, options = {}) {
options.archive = true;
result.push(
...pagination(path, posts, {
perPage,
layout: ["archive", "index"],
format: paginationDir + "/%d/",
data: options,
})
);
}
// ......我们可以看到
hexo-generator-achive
返回数据的layout
指定了archive
作为模板文件渲染。所以生成的 archives/index.html 自然利用了 theme/layout/archive.ejs 进行了代码的生成。为什么 tags/index.html 没有生成,/tags/tag-a.html 却生成了,并且利用了 theme/tag.ejs 这个模板?
解答这个问题需要查看
hexo-generator-tag
的代码: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//......
// generate tag index page, usually /tags/index.html
if (config.tag_generator.enable_index_page) {
tagDir = config.tag_dir;
if (tagDir[tagDir.length - 1] !== "/") {
tagDir += "/";
}
pages.push({
path: tagDir,
layout: ["tag-index", "tag", "archive", "index"],
posts: locals.posts,
data: {
base: tagDir,
total: 1,
current: 1,
current_url: tagDir,
posts: locals.posts,
prev: 0,
prev_link: "",
next: 0,
next_link: "",
tags: tags,
},
});
}
//......我们可以看到,如果
config.tag_generator.enable_index_page
为空或者没有定义,就不会生成path
的值是/tags/index.html
。然后再查看生成 /tags/tag-a.html 的代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15const pages = tags.reduce((result, tag) => {
if (!tag.length) return result;
const posts = tag.posts.sort(orderBy);
const data = pagination(tag.path, posts, {
perPage: perPage,
layout: ["tag", "archive", "index"],
format: paginationDir + "/%d/",
data: {
tag: tag.name,
},
});
return result.concat(data);
}, []);其中
layout
数组中,第一个指定了tag
作为模板,所以只要在我们theme/layout/
文件夹下面找到了tag.ejs
就会使用该文件去渲染具体的 tag 页面。
- 本文链接: https://tobelight.github.io/2024/hexo-generator/
- 版权声明: 本博客所有文章采用 BY-NC-SA 许可协议。