前言

官方文档

自己的博客运行了也有一段时间了,想了解一下自己的博客站点整体是如何工作的?以及自己可以做点什么,来往这个博客站点中植入自己的插件来满足自定义的需求!
本文将具体分析一下hexo是如何工作的?它都有哪些组成?我们可以在这个框架上如何自定义自己的需求?以及这个框架给我们代码的可学习的地方!!!

hexo的组成

首先,不管怎么,代码到手,天下我有! 👉 先来看一下对应的hexo的代码目录结构:
hexo的代码组织结构
👾 通过package.json中的main,可以发现其中的入口在于bin/hexo文件

1
2
3
#!/usr/bin/env node
'use strict';
require('hexo-cli')();

👾hexo则是调用的hexo-cli库来实现的,后面发现,hexo-cli则是反过来调用的hexo来进行 👉 创建一个Hexo对象,并最终调用这个Hexo对象的init()方法 !!
hexo的执行顺序

🌠 也就是说这个hexo-cli充当了一个执行者的作用!!
hexo的构成

👽 而这里的Hexo对象,则是继承于EventEmitter对象,可针对其过程进行一系列的监听(也就是监听其生命周期过程方法)!
Hexo对象的组成

1. extend扩展(以console.clean为例)

Hexo扩展对象.png
从上面可以看出,Hexo通过其extend对象中的各个属性,对外提供的可随意针对不同阶段进行扩展自定义业务的机制,也就是我们可以编写自己的插件来交付给hexo,让他来帮执行我们的需求!!

👉 这里我们仅分析一个插件,他是如何来编写以及如何被植入到扩展中的(以console为例)!!
注册的console的clean命令.png
😕 这里的console.register()方法,他做的一个怎样的动作的呢??来看一下对应的register()函数:
Console插件服务
👇 是对应的简写版的register()函数内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// this.store = {};
// this.alias = {};
/**
* @name: 注册的hexo命令名称
* @desc: 对该命令的一个描述
* @options: 该命令所需的参数
* @fn: 该命令所对应的执行动作
*/
register(name, desc, options, fn){
//... 此处隐藏一系列的参数个数兼容赋值操作
if(fn.length > 1){
fn = Promise.promisify(fn);
}else{
fn = Promise.method(fn);
}
const c = fn;
this.store[name.toLowerCase()] = c;
c.options = options;
c.desc = desc;
this.alias = abbrev(Object.keys(this.store));
}

这里的register()方法对外提供了一个方法,用于将外部插件fn以及对应的描述和参数通过abbrev生成关键词参数,存储在store对象中,以便于后续Hexo拿来使用!

🌠 这里的abbrev三方库:可将一系列字符串参数转换为每个单词依次追加的对象,主要用于命令行的命令声明使用,如下图说明所示:
abbrev的效果

🌠 而于此同时,我们所传递进来的fn它可以是一个函数数组,也可以是一个普通的函数,如果传递的函数数组则表示我们将在这个命令上计划执行N个按照数组顺序排列执行的函数,每一个函数都是一个个待被执行的promise

👇 是对应的clean.js的程序内容!

1
2
3
4
5
6
7
8
9
function cleanConsole(args) {
return Promise.all([
deleteDatabase(this),
deletePublicDir(this),
this.execFilter('after_clean', null, {context: this})
]);
}
//**** 此处隐藏一系列代码 ****
module.exports = cleanConsole;

🌠 针对上述的代码进行一个简单的分析,通过对外暴露一个方法函数,该函数接受一个args参数,执行3个动作:

  1. 删除Database数据库;
  2. 删除public目录;
  3. 触发过滤器after_clean,并传递当前的上下文对象

这里有一需要 的地方,在插件中直接调用的this.execFilter方法😕 那么这里的this指向的是谁呢?
插件所需的上下文是如何传递进来的
Hexo注册插件的过程

2. warehouse本地json数据库

具有模型、模式和灵活查询接口的 JSON 数据库,可以理解为是一个JSON文件,然后可通过其API来对其进行增删改查操作!

官方文档

🌠 先来看一下这个warehouse的一般用法:
warehouse的一般使用
👾 与一般的db操作类似,创建db的引用(类似于连接),然后关联对应的数据表Schema,然后就可针对不同的数据表Schema进行增删查改操作,然后需要将操作缓存到json数据库中(这个save动作必须要执行,否则对应的json文件将没有任何的用处)!

😕 那么这个warehouse数据库的结构组成是怎样的呢?我们可以先看一下 👇 在内存中的数据库的内容形式:
warehouse中的database存储形式

🌠 结合实际的源码,我们可以得出 👇 的一个warehouse的组成结构图:
Warehouse数据库

warehouse所支持的数据表字段类型SchemaType有:

数据表字段 描述
array 数组类型
boolean 布尔值类型
buffer 文件缓存类型
cuid 随机id类型
date 日期
integer 整型类型
number 数值类型
object 对象类型

那么我们在定义数据表的时候, 😕 假如需要将对应表的字段给声明出来,不同的表对应用不同的Schema来表示,那么假如我们需要定义不同类型的字段,应该如何来声明呢?

1
2
3
4
5
6
db.model('customs2', new Schema({
cate: { type: Enum, value: [1,2,3,4] },
content: { type: Object, default: {} },
list: { type: Array, default: [] },
c: { type: CUID }
}));

不同参数字段的数据表

👽 在实际的coding过程中,我们还可以将Model的创建与数据的管理给分离开来:
database与model分开维护的方式

hexo中是如何来运用这个warehouse的呢?
hexo如何使用warehouse的
这里其实与我们平时使用类似,都是面向接口编程,统一操作database,由database自行去维护好与model之间的一个关联关系!

3. render渲染引擎(hexo server)

当我们执行的hexo server命令的时候,从 的学习可以得知,最终会执行到Hexo.call()方法,该方法主要负责从console已注册的扩展动作中取出函数来执行!,如下代码所示:

1
2
3
4
5
6
call(name, args, callback) {
const c = this.extend.console.get(name);
// 以Reflect的方式,来执行一个fn,并传递参数设置回调callback
if (c) return Reflect.apply(c, this, [args]).asCallback(callback);
return Promise.reject(new Error(`Console \`${name}\` has not been registered yet!`));
}

😖 这里我们并没有在Console中找到关于hexo server对应的函数实现是怎样的,实际上这也是hexo实现插件化编程的灵活之处,将对于这个命令的实现,交由hexo-server另外一个库来实现!!!

hexo-server的实现过程

插件如何编写

一切在hexo中运行的插件,都可以直接访问到hexo上下文对象
由于可以直接访问到hexo上下文对象,因此可以直接针对hexo.extend来进行额外动作的补充

1
2
3
hexo.extend.console.register('server', 'Start the server.', {
// ... 此处隐藏相关的options的定义
}, require('./lib/server'));

因此,执行的hexo server其实就是调用的lib/server方法!
👉 而这里的动作无非也就是监听html相关文件的生成,并借助于connect + serve-static来监听运行在端口上的静态网站站点!!

能够做点什么

在学习完成了hexo的相关知识点后,发现有不少可值得学习的地方:

1. 针对扩展新增自定义插件(新增的插件都是不能访问浏览器资源的)

2. 优化现有的站点

学到的东西

1. 模仿nodejs实现自定义的函数包裹,并从中获取额外的上下文对象

hexo模仿的nodejs追加的module以及export对象

2. 对外暴露extend对象代表不同阶段的扩展,实现插件编程范式

3. 本地简易数据库warehouse-json编程

可以将当下的后台一些无须复杂数据库方能实现的逻辑,交由warehouse来实现!!