前言

注意!根据butterfly版本的不同,homepage.styl的代码可能也会不同,本文所使用的主题为4.4.0版本

其实我在创建博客时一直想做到的就是在主页将文章和说说放一起,类似于习泯小点的效果,但之前一直搜不到相关教程,于是作罢。
最近因为蓝桥杯想写个博客记录一下,先整了个自动上传并部署博客的python脚本,又发现leancloud国际版g了,于是便有了这篇文章。

过程

我将工作分为两部分:第一是首页显示的样式要与文章不同,
第二是不能有说说的归档和路径等一切正常文章具备的。

第一步在浏览了一个插件后得知只要修改post-ui.pug即可。
而第二步对我来说就没有什么头绪了,我便在网上搜索讲解hexo原理的文章。
以下是我在查看文章的碎碎念。

主页、归档、分类等文章聚合页面的静态文件是由generator生成的
源码中的model insert等关键字均与warehouse数据库有关

在看过了一些文章后我认为hexo g指令的逻辑应该是处理器→生成器→渲染器,由渲染器生成html,那么我就想只要不让渲染器渲染,post的信息应该还会存在于warehouse数据库中,但不会有说说的静态文件生成。

初步查看hexo源码后打算在plugins/generator/post.js中修改,尝试从列表中把文章删除,并没有起效果,而且我想了下发现就算成功了也不是我想要的效果。

在进一步查看hexo源码后发现hexo-core的lib/plugins/console/generate.js里面有io操作,而且我认为这应当是hexo generate执行的操作。

1
2
3
4
5
6
7
8
9
10
11
const task = (fn, path) => () => fn.call(this, path);
const doTask = fn => fn();
const routeList = route.list();
const publicFiles = Cache.filter(item => item._id.startsWith('public/')).map(item => item._id.substring(7));
const tasks = publicFiles.filter(path => !routeList.includes(path))
// Clean files
.map(path => task(this.deleteFile, path))
// Generate files
.concat(routeList.map(path => task(this.generateFile, path)));

return Promise.all(Promise.map(tasks, doTask, { concurrency: parseFloat(concurrency || 'Infinity') }));

doTask(task(this.generateFile, path))最后达成的效果即generateFile(path),所以这段代码其实是将所有要generate或delete的文件放入数组并依次执行对应操作。

看到源码里有个route.list()我接下来便开始寻找route是什么,通过文件搜索发现list()方法只存在于/lib/hexo/router.js,于是我使用log.info(route.constructor.name)发现就是router.js里面的Router类,里面有set和remove方法,于是我想可以在生成静态文件之前就把路径从route里面删除,这样便不会生成静态文件,发现从route里面删除后generate会自动删除对应的index.html,但在首页和归档里仍会显示,首页是需要留着的,剩下就是归档里如何解决。

在Router类里面的set方法中发现data有两种类型

1
2
3
4
5
6
7
8
9
10
let obj;

if (typeof data === 'object' && data.data != null) {
obj = data;
} else {
obj = {
data,
modified: true
};
}

通过输出data.toString()发现data类型为对象的是从lib/plugins/generator/asset.js里传来的,
类型不为对象的data从lib/hexo/index.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
const createLoadThemeRoute = function(generatorResult, locals, ctx) {
const { log, theme } = ctx;
const { path, cache: useCache } = locals;
const layout = [...new Set(castArray(generatorResult.layout))];
const layoutLength = layout.length;
// always use cache in fragment_cache

locals.cache = true;

return () => {
if (useCache && routeCache.has(generatorResult)) return routeCache.get(generatorResult);

for (let i = 0; i < layoutLength; i++) {
const name = layout[i];
const view = theme.getView(name);
if (view) {
log.debug(`Rendering HTML ${name}: ${magenta(path)}`);
return view.render(locals)
.then(result => ctx.extend.injector.exec(result, locals))
.then(result => ctx.execFilter('_after_html_render', result,{
context: ctx,
args: [locals]
}))
.tap(result => {
if (useCache) {
routeCache.set(generatorResult, result);
}
}).tapCatch(err => {
log.error({ err }, `Render HTML failed: ${magenta(path)}`);
});
}
}
log.warn(`No layout: ${magenta(path)}`);
};
};

在浏览此文件时发现此文件里也有关于generate的函数,此函数会遍历所有的生成器,最后又是回到hexo-generator-archive这个插件上,注意到插件获取文章是通过传入的locals参数获取的,进行溯源后发现仍然在lib/hexo/index.js里,文件存在_runGenerators函数,者是运行所有生成器的函数,传入的locals也在这里生成。修改后得

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
_runGenerators() {
    this.locals.invalidate();
    const siteLocals = this.locals.toObject();
    const generators = this.extend.generator.list();
    const { log } = this;

    let originData = siteLocals.posts.data
    let newData = [];
    for(let i =0;i < siteLocals.posts.data.length;i++) {
      if(!siteLocals.posts.data[i].shuoshuo){
        newData.push(siteLocals.posts.data[i]);
      }
    }

    // Run generators
    return Promise.map(Object.keys(generators), key => {
      const generator = generators[key];
      if (key !== 'index') {
        siteLocals.posts.data = newData;
        siteLocals.posts.length = newData.length;
      } else {
        siteLocals.posts.data = originData;
        siteLocals.posts.length = originData.length;
      }

      return Reflect.apply(generator, this, [siteLocals]).then(data => {
        log.debug('Generator: %s', magenta(key));
        return data;
      });
    }).reduce((result, data) => {
      return data ? result.concat(data) : result;
    }, []);
  }

虽然上面这些代码有点蠢,但以我的知识水平也只能想到这种解决办法,只能说确实是没系统学过js。

经过修改后如果文章有名为”shuoshuo”的frontmatter,那么说说只会在主页出现,最近文章、统计数据里都不会有说说,最后的工作即对主页样式的修改,以下代码均从butterfly主题原版代码复制修改而来。

1
2
3
4
5
6
7
8
9
10
11
12
 mixin postUI(posts)
  each article , index in page.posts.data
+     if article.shuoshuo
+      .shuoshuo
+        .shuoshuo-info
+           .shuoshuo-date-wrap
+             span.shuoshuo-date
+               i.far.fa-calendar-alt
+               span.shuoshuo-date-label='发布于'
+               time.shuoshuo-meta-date-created(datetime=date_xml(article.date))=full_date(article.date)
+           .content!= article.content
+     else

node_modules/hexo-theme-butterfly/layout/includes/mixins/post-ui.pug中加入以上代码并从.recent-post-itemif theme.ad && theme.ad.index向后缩进两个空格

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
  #recent-posts

  & > .recent-post-item:not(:first-child)
    margin-top: 20px

& > .shuoshuo:not(:first-child)
margin-top: 20px

+   & > .shuoshuo
+     @extend .cardHover
+     display: flex
+     flex-direction: column
+     align-items: flex-start
+     overflow: hidden
+
+   +maxWidth768()
+       flex-direction: column
+       height: auto
+
+     & > .shuoshuo-info
+       padding: 20px 40px
+       width: 100%
+
+       +maxWidth768()
+         padding: 20px 20px 30px
+         width: 100%
+
+       & > .shuoshuo-date-wrap
+         margin: 6px 0
+         color: $theme-meta-color
+         font-size: 90%
+
+         i
+           margin: 0 4px 0 0
+
+         & > .shuoshuo-date
+           cursor: default
+
+         .shuoshuo-date-label
+           if hexo-config('post_meta.page.label')
+             padding-right: 4px
+           else
+             display: none

  & > .recent-post-item
    @extend .cardHover
    display: flex
    flex-direction: row
    align-items: center
    overflow: hidden
    height: 18em

node_modules/hexo-theme-butterfly/source/css/homepage.styl中加入以上代码,运行hexo g即可生效。

总结

此次魔改花费了我很长时间,寻找关键代码,一处一处插入console.log都颇为耗时。期间还曾想过用插件实现,但在搜索和尝试查找是否有类似插件后我放弃了这个念头。

在魔改前,我还信心满满,觉得自己学会了点js的皮毛,也能看懂一点nodejs,已经不是那时候一步步按教程来创建博客的我了,以为能很轻松完成这个事情,结果到现在连hexo的原理都没搞明白,所以人还是要谦虚,不能有点小成果便自满,还是要不断学习不断进行才行。