前言
注意!根据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))
.map(path => task(this.deleteFile, path))
.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; 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]); } }
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-item
到if 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的原理都没搞明白,所以人还是要谦虚,不能有点小成果便自满,还是要不断学习不断进行才行。