惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

V
V2EX
C
Check Point Blog
博客园_首页
B
Blog
D
Docker
U
Unit 42
量子位
I
InfoQ
有赞技术团队
有赞技术团队
Martin Fowler
Martin Fowler
GbyAI
GbyAI
L
LangChain Blog
云风的 BLOG
云风的 BLOG
博客园 - Franky
美团技术团队
T
The Blog of Author Tim Ferriss
阮一峰的网络日志
阮一峰的网络日志
月光博客
月光博客
Vercel News
Vercel News
Recent Announcements
Recent Announcements
雷峰网
雷峰网
大猫的无限游戏
大猫的无限游戏
小众软件
小众软件
Google DeepMind News
Google DeepMind News

贰猹の小窝

个人哔哔: 谈薪时间-该问hr哪些问题 个人哔哔: 秋招与未来 个人哔哔: 2022年度总结与全新的大饼 猹的魔改日记-文章页抛弃卡片、丢弃小菜单以及布局重构 猹的魔改日记-小小的重写个页脚 回忆录: 知心友与白月光 rust写个操作系统——课程实验blogos移至armV8深度解析:实验八下 内存管理 rust写个操作系统——课程实验blogos移至armV8深度解析:实验八上 内存管理 HNU工训综合项目二:基于485总线的评分系统速通指北 rust写个操作系统——课程实验blogos移至armV8深度解析:实验七 死锁与简单处理 rust写个操作系统——课程实验blogos移至armV8深度解析:实验六 GPIO关机 rust写个操作系统——课程实验blogos移至armV8深度解析:实验五 输入 rust写个操作系统——课程实验blogos移至armV8深度解析:实验四 中断 rust写个操作系统——课程实验blogos移至armV8深度解析:实验二 Hello World 关于linux: 设备树浅析(更新中) rust写个操作系统——课程实验blogos移至armV8深度解析:实验一 环境配置 rust写个操作系统——课程实验blogos移至armV8深度解析 猹的魔改日记-小菜单和复制/F12提示栏 个人哔哔: 一年期至,又一年始(不定时写完)
从零开始的异世界主题开发生活-hexo事件管理与扩展使用
noionion · 2023-01-05 · via 贰猹の小窝

前言

想起一年半以前,想要对我的初版博客进行整体的魔改,然后设计了我的首版卡片(并不会写但是设计粗来了)。然而一年半过去了,我的博客经历了大大小小的魔改调整,文章页改完了卡片反而觉得不搭了。改着改着又不太喜欢文章卡片式设计等等。

种种原因到我想放弃 butterfly。用店长的话说就是,魔改到现在也不算在坚守 bf 了。

于是我开始寻思着要不要换主题,而 stellar 很合我心意,但又有我很中意但又太复杂且定位不符的 wiki 功能。于是思来想去,决定自己开发一个主题吧,也学一学自己拒绝了两年的前端。

废话完了,开始讲记录吧。hexo 主题的开发对于我这个前端半生不熟的人来讲还是很有难度的。本身也没有画过时间系统的学习前端。js 最多就是魔改 bf 的时候稍微模仿着写一点点的程度,css 也就只有常用的一些相对熟悉一点,动画、布局、定位这些毫无经验。因此记录是必要,本身写项目就是一种很好的学习方式。

另外记录的是 hexo 本身开发的经历,给一些不满足于魔改的想开发主题的朋友做做参考也行。

友情感谢 @mufanc 同学在开发 rust 博客框架途中被我拐来(甚至弃坑)trefoil 主题的开发,这里着重感谢!


hexo 开发常用-事件系统与扩展api使用

事件: hexo.on

hexo 的文档可谓一塌糊涂,令人开发都不知道何处动笔,只能参考前人主题去做些尝试。当还没确定布局的时候,需要想好自己要哪些脚本进行辅助构建。hexo.on 提供的事件钩子可以让我在一些阶段进行一些数据的构建(例如 stellar 的 wiki 数据结构),也可以在使用 hexo 的时候出现 banner 来体现使用了自己的主题。

hexo 事件文档

比如在 hexo 运行开始,提示一条主题使用语句:

hexo.on('ready', () => {
const { version } = require('../../package.json')
hexo.log.info(` Welcome to trefoil! ${version} `)
})

运行出现如下字样:

PS C:\MyPersonProject\blog\theme-trefoil-demotest> hexo s
INFO Validating config
INFO Welcome to trefoil! 0.0.1
INFO Start processing
INFO Hexo is running at http://localhost:4000/ . Press Ctrl+C to stop.

而在主题开发中,最常用的是 generateBeforegenerateAfter 这两个事件。以 generateBefore 为例:

例如我想在 trefoil 主题中构建一个轻量化的类似 wiki 的合集功能,需要在文章渲染前对所有原始 maekdown 文件的 front-matter 进行统计并构建对象,就需要在这个阶段进行构建(其实可以在过滤器相似的阶段来构建,但这明显不是过滤器应该做的事),然后将其设为全局变量供文章渲染阶段使用。

hexo.on('generateBefore', () => {
const series_dist = {}
const posts = hexo.locals.get('posts')

hexo.locals.set('series_dist', () => series_dist)
})

过滤器 hexo.extend.filter

过滤器是 hexo 中主观上觉得 api 相对完全的一个功能(因为可以说只有它有完整的调用事件列表,相比 hexo.on 能干的事感觉多了不少),甚至也只能在过滤器中去干一些感觉就不应该由它做的事。

前人在过滤器最常见使用的两个功能是修改渲染前/后文件和选择性生成css。后者我还没使用到,前者倒是已经实战了一下。

比如渲染前加入 abbrlink:(这部分代码是 mufanc 帮我写的,我为了合集功能做的主副标题特性与现有的 abbrlink 插件不兼容)

hexo.extend.filter.register('before_post_render', function (data) {
if (data.abbrlink || data.layout !== 'post') return

const config = this.theme.config.abbrlink || this.config.abbrlink || {};
if (!config.enable) return

const mirror = front.parse(data.raw);

const abbrlink = hash(data.title, data.subtitle, data.series_name, data.series_index);
mirror.abbrlink = abbrlink;
data.abbrlink = abbrlink;

fs.writeFileSync(data.full_source, '---\n' + front.stringify(mirror), 'utf-8')

hexo.log.info(`Generate link [${abbrlink}] for post [${data.source}][${data.title+(data.subtitle?(': '+data.subtitle):'')}]`);
}, 15);

它向渲染前的原始文件开头的 front-matter 加入了 abbrlink 参数。而下面这个栗子则是向渲染后的 html DOM 进行代码插入,我需要向代码块中插入几行行代码来实现代码复制和代码展开功能:

hexo.extend.filter.register('after_post_render', function (data) {
const config = this.theme.config
if (config.code.height_limit_enable) {
if (!data.permalink.match(/.*(?:\.html|\/)$/)) {
return;
}

let $ = cheerio.load(data.content)
$('<figcaption></figcaption>').insertBefore('figure.highlight table:not(figcaption + *)')
$('figure.highlight figcaption').append('<i class="ri-file-copy-line"></i>')
$('figure.highlight table').before('<div class="code-expand-btn"><i class="ri-arrow-down-s-fill"></i></div>')
data.content = $.html();
}
});

原先的 DOM 结构如下:

<figure class='highlight'>
<figcaption> /*可能不存在的标题*/
<span>标题</span>
</figcaption>
<table>
<tr>
<td class='gutter'>...</td> /*可能不存在的序号*/
<td class='code'><pre>...</pre></td> /*代码块*/
</tr>
</table>
</figure>

经过过滤器修改后的结构如下:

<figure class='highlight'>
<figcaption> /*必定存在的figcaption*/
<span>标题</span> /*可能不存在的标题*/
<i class="ri-file-copy-line"></i> /*复制图标*/
</figcaption>
<div class="code-expand-btn"><i class="ri-arrow-down-s-fill"></i></div> /*展开图标*/
<table>
<tr>
<td class='gutter'>...</td> /*可能不存在的序号*/
<td class='code'><pre>...</pre></td> /*代码块*/
</tr>
</table>
</figure>

足见过滤器功能之强大。

生成器 hexo.extend.generator

生成器用于生成文章路由,比如我们无需创建 md 文件就可以利用模版生成 404 页面:

hexo.extend.generator.register('404', function (locals) {
return {
path: '/404.html',
layout: ['404']
}
});

这部分功能也还没怎么用。

辅助函数 hexo.extend.helper

辅助函数帮助您在模板中快速插入内容,有点类似与 pug 中的 mixin。但是显然用 js 功能会更加强大一点,同时也可以与前面我们生成的合集数据结构对接。

例如从合集数据结构中生成合集目录供模板调用:

hexo.extend.helper.register('__series_toc', function (page) {
const series_dist = hexo.locals.get('series_dist')
const toc_str = []

return toc_str.join('')
})

之后我就可以在文章页模版使用辅助函数进行调用:

!=__series_toc(page)

结语

文档不能不吐槽,api 提供的我感觉 hexo 的项目结构也不咋合理。但能用还是别自己造轮子了,起码能用。。。