浮生逆旅

Hexo 之 Html 重命名

字数统计: 1.9k阅读时长: 9 min
2019/10/23 Share

为什么去做?

诚然, Hexo 为我们提供了快速易懂搭建个人博客的选择,但由于个人英文水平实在有限,尤其需要通过 Markdown 的文件标题来描述每篇文章都是什么内容这情况下,使用蹩脚的英文去命名每篇博客,我总是觉得有些难受,而偏偏 Hexo 生成的 Html 文件名称,就是由 Markdown 文件名称来决定的,中文的 URL 显示在地址栏中,未免有些别扭。源于这些原因,我决定使用自己的规则来生成 Html 文件名称。


原则

主要有两个点:

  • 生成的 Html 静态网页不能每次执行hexo generate时都发生改变 以免影响到已经分享出去的文章
  • 维持现有的功能,如 Tags、Categories 下的文章 href 链接

实现

按规则生成 Html

Html 生成的触发操作是hexo generate,首先我们要找到这个操作的入口

1
2
3
4
5
6
7
8
> which hexo
/Users/verycooltop/.nvm/versions/node/v10.16.3/bin/hexo
> cat /Users/verycooltop/.nvm/versions/node/v10.16.3/bin/hexo
#!/usr/bin/env node

'use strict';

require('hexo-cli')();

然后,我们再到 hexo 所在目录下的 node_modules 下的 hexo-cli

1
2
3
4
5
6
7
8
9
> cd /Users/verycooltop/.nvm/versions/node/v10.16.3/bin/
> ll
total 81632
# ...
lrwxr-xr-x 1 verycooltop staff 33B 10 18 16:54 hexo -> ../lib/node_modules/hexo/bin/hexo

> cd ../lib/node_modules/hexo/node_modules/hexo-cli
> pwd
/Users/verycooltop/.nvm/versions/node/v10.16.3/lib/node_modules/hexo/node_modules/hexo-cli

找到 hexo-cli main 入口文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
> cat package.json
{
"_from": "hexo-cli@^3.0.0",
"_id": "hexo-cli@3.1.0",
...
"main": "lib/hexo",
...
"scripts": {
"eslint": "eslint .",
"test": "mocha test/index.js",
"test-cov": "nyc npm run test"
},
"version": "3.1.0"
}

hexo-cli/lib/hexo.js 最终使用的 hexo 模块还是我们自己博客项目下的 node_modules/hexo 模块

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
'use strict';

const chalk = require('chalk');
const tildify = require('tildify');
const Promise = require('bluebird');
const Context = require('./context');
const findPkg = require('./find_pkg');
const goodbye = require('./goodbye');
const minimist = require('minimist');
const resolve = require('resolve');
const camelCaseKeys = require('hexo-util/lib/camel_case_keys');

class HexoNotFoundError extends Error { }

function entry(cwd = process.cwd(), args) {

// ...

// 通过 findPkg 及 loadModule require hexo 模块
return findPkg(cwd, args).then(path => {

if (!path) return;

hexo.base_dir = path;

return loadModule(path, args).catch(() => {
log.error('Local hexo not found in %s', chalk.magenta(tildify(path)));
log.error('Try running: \'npm install hexo --save\'');
throw new HexoNotFoundError();
});
}).then(mod => {
if (mod) hexo = mod;
log = hexo.log;
// ...
function loadModule(path, args) {
return Promise.try(() => {
const modulePath = resolve.sync('hexo', { basedir: path });
const Hexo = require(modulePath);

return new Hexo(path, args);
});
}
// ...
module.exports = entry;

之后回到我们自己的博客项目,找到 hexo 模块的的 main 入口文件


修改 blog/node_modules/hexo/lib/hexo/index.js

每次生成后的 html 文件名称要始终要保持一致,并且不能与其他生成文件名称产生冲突,所以我决定使用 source/_posts 下 md 文件中 date 属性 format(YYYYMMDDHHmmss) 后的字符串作为 html 文件名称。

1
2
3
4
5
6
7
8
9
10
11
---
title: Hexo 之 Html 重命名
date: 2019-10-23 20:10:17
tags:
- Fork
- Code
categories: Hexo
keywords:
- hexo
- hexo rename
---

当然,大家可以按照自己的规则去生成 html 的文件名称,前提是需要有一个唯一标识,每次都能够拿到这个唯一标识,并且每次进行同样计算可以得到唯一结果。

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
'use strict';

const Promise = require('bluebird');

// ...

Hexo.prototype._routerReflesh = function(runningGenerators, useCache) {
const { route } = this;
const routeList = route.list();
const Locals = this._generateLocals();
Locals.prototype.cache = useCache;

return runningGenerators.map(generatorResult => {
if (typeof generatorResult !== 'object' || generatorResult.path == null) return undefined;

// add Route
// 修改前
// const path = route.format(generatorResult.path);
// const { data, layout } = generatorResult;

// 修改后
let path = route.format(generatorResult.path);
const { data, layout } = generatorResult;
// generate YYYYMMDDHHmmss.html
if (layout && layout[0] === 'post') {
path = `${data.date.format('YYYYMMDDHHmmss')}.html`;
}


if (!layout) {
route.set(path, data);
return path;
}

// ...

module.exports = Hexo;

这样,我们再次执行 hexo generate 就可以生成按照我们规则定义名称的 html 了


使页面串联

通过hexo generate我们可以看到已经生成我们想要的 html 文件名称,但public/index.htmlarchiivestags中所有链接的href属性目前还是以往规则生成的页面名称,现在我们需要对渲染模板进行修改。

首页

themes/archer/layout/index.ejs

1
2
3
4
5
6
7
8
9
10
11
12
<!-- //... -->
<% _content = truncateLength === 0 ? null : truncate(strip_html(_post.content), {length: truncateLength, omission: '...'})%>
<% } %>
<article class="index-post">
<!-- =====修改前 -->
<!-- <a class="abstract-title" href = "<%- url_for(_post.path) %>" > -->
<!-- =====修改后 -->
<a class="abstract-title" href = "<%- url_for(_post.date.format('YYYYMMDDHHmmss')) %>.html" >
<% if(_post.top) { %>
<span class="stick-top iconfont-archer">&#xe63d;</span>
<% } %>
<!-- // ... -->

Archives

themes/archer/layout/_partial/sidebar/sidebar-archives.ejs

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
<div class="sidebar-panel-archives">
<!-- 在ejs中将archive按照时间排序 -->
<% var resortAllPosts = site.posts.map(function (item) { %>
<% let title = item.title; %>

<!-- =====修改前 -->
<!-- <% let link = url_for(item.path); %> -->
<!-- =====修改后 -->
<% let link = date(item.date, "YYYYMMDDHHmmss") + ".html"; %>

<% let _date = date(item.date, "YYYY-MM-DD HH:MM:SS"); %>
<% let returnDate = {
title : title,
link : link,
date : _date }; %>
<% return returnDate; %>
<% }) %>

<!-- // ... -->

<% currPagePosts.forEach(function (post) { %>
<% if (showYear != Number(date(post.date, "YYYY"))) { %>
<% showYear = date(post.date, "YYYY"); %>
<% if (toCloseUl === 1) { %>
<% toCloseUl = 0; %>
</ul>
<% } %>
<div class="archive-year"> <%= showYear %> </div>
<ul class="year-list">
<% toCloseUl = 1; %>
<% } %>
<li class="archive-post-item">
<span class="archive-post-date"><%- date(post.date, "MM/DD") %></span><a class="archive-post-title" href= "<%- post.link %>" ><%- post.title || '[Untitled Post]' %></a>
</li>
<% }) %>
</div>
</div>

Tags

themes/archer/src/js/tag.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// ...
let $aItem = $(
'<a class="meta-post-title" href="' +
siteMeta.root +
postInfo.path +
'">' +
// 修改前
/**
* postInfo.path +
* '">' +
*/
// 修改后
archerUtil.dateFormater(
new Date(Date.parse(postInfo.date)),
'yyyy/MM/dd'
) +
'.html">' +
postInfo.title +
'</a>'
)
// ...

Post

themes/archer/layout/post.ejs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!-- // ... -->
<% if(theme.copyright.enable === true && page.copyright !== false) { %>
<div class="license-wrapper">
<p><%- __('author') %><a href="<%- config.url %>"><%- config.author %></a>
<!-- =====修改前 -->
<!--
<p>原文链接:<a href="<%- page.permalink %>"><%- page.permalink %></a>
<p>发表日期:<a href="<%- page.permalink %>"><%- (page.date).format('MMMM Do YYYY, h:mm:ss a') %></a>
<p>更新日期:<a href="<%- page.permalink %>"><%- (page.updated).format('MMMM Do YYYY, h:mm:ss a') %></a>
-->
<!-- =====修改后 -->
<p>原文链接:<a href="/<%- page.date.format('YYYYMMDDHHmmss') %>.html"><%- (page.date).format('MMMM Do YYYY, h:mm:ss a') %></a>
<p>发表日期:<a href="/<%- page.date.format('YYYYMMDDHHmmss') %>.html"><%- (page.date).format('MMMM Do YYYY, h:mm:ss a') %></a>
<p>更新日期:<a href="/<%- page.date.format('YYYYMMDDHHmmss') %>.html"><%- (page.updated).format('MMMM Do YYYY, h:mm:ss a') %></a>

<p>版权声明:<%- theme.copyright.license %></p>
</div>
<% } %>
<!-- // ... -->

需要注意下,我使用的 themeArcher,每个 theme 使用的引擎模版也可能不尽相同, Archer 使用的是 EJS 模板引擎,我不能保证其他 theme 所需要修改的代码是上面我所说的。不过大体思路是修改首页ArchivesTags每篇博客 中会出现的文章链接。

最后

完成上面的修改后,我们就可以运行我们的博客,来体验下整齐 URL 所带来的舒爽感了。

因为我修改了 Hexo 框架的源代码,也使用了 Jenkins + Github 来做博客的部署工作(你要问我为什么不使用官方推荐的部署方式,那是因为想熟悉下 Jekins,哈哈),所以我 Fork 了 Hexo,我依赖的是修改之后的 Hexo

接下来的话,我可能考虑再弄一下 Sitemap,Google 的和 Baidu 的,同样,我应该也要 Fork 她们。

拜~


CATALOG
  1. 1. 为什么去做?
    1. 1.1. 原则
  2. 2. 实现
    1. 2.1. 按规则生成 Html
      1. 2.1.1. 修改 blog/node_modules/hexo/lib/hexo/index.js
    2. 2.2. 使页面串联
      1. 2.2.1. 首页
      2. 2.2.2. Archives
      3. 2.2.3. Tags
      4. 2.2.4. Post
  3. 3. 最后