hexo源码分析(一)
其实是由一个小问题引发的,在配置icarus主题的时候,发现的一个问题,类似这个链接。就是hexo-renderer-inferno
这个插件没有正常运行导致的,但是node_modules
中可以很明显的看到,作为hexo-theme-icarus
依赖的依赖,是被正常的安装下来了,按照文中的解决方案虽然能解决,但是并没有真正从源头上解决。基于这个原因,想从hexo的源码中看一看插件加载机制。
很明显搜索引擎按照hexo 源码
作为关键字的搜索结果中,并没有真正的源码分析,都是非常浅层的解析。所以这里我将从源码的角度尝试分析一下hexo内部的运行机制。
注:hexo的版本"version": "5.4.0"
导读
这里将分几篇文章把我在阅读hexo代码的过程记录一下,作为hexo源码阅读第一篇文章,将分析一下hexo-cli的设计实现思路。
输入 hexo 发生了什么?
和其他的脚手架一样,首先是从package.json
中的bin
字段开始的。bin有什么用可自行去npm文档查看。
hexo的bin
我们在安装完npm i hexo
之后,可以看到hexo的package.json
中的bin
字段如下
1 | "bin": { |
此时,执行 hexo命令之后,后续就会被bin/hexo
接管,可以看下bin/hexo
的内容,直接使用了hexo-cli
暴露的方法。
1 | #!/usr/bin/env node |
hexo-cli做的事情
接下来我们来看一看hexo-cli
做了什么,直接找 package.json
中的 main字段,找到入口文件lib/hexo.js
,接下来执行hexo ...
都会被这里接管。
hexo-cli
和其他cli工具类似,里面的实现并不复杂,大致看一下就能了解实现原理,如果想了解一般cli的执行流程,你可以看我之前写的 vue-cli源码学习2.x,里面分析了vue-cli版本2的实现原理。
hexo-cli
提供了init
、help
、version
这三个命令,那么此时你一定会有一个疑惑,hexo server
、hexo g
等等命令是怎么执行的?hexo-cli和hexo两个依赖的关系是怎么样的?
其实我的最大的疑问也在这里,因为按照cli的惯例,一般所有命令都会在cli中实现对应的逻辑,但是经过调试发现,hexo似乎并不想这么做,而是把其他的命令逻辑放到hexo这个依赖中了。我理解是hexo作为一个暴露出来的核心类,里面的逻辑会很复杂,所以单独抽离出来了,hexo-cli
只是提供了很简单的一个功能。那么我们接下来看。
1 | // hexo-cli/lib/hexo.js |
上面这段逻辑就是实例化Hexo的关键步骤,第一遍看的时候没发现,后来才发现这里,这个就是hexo-cli
和hexo
这两个依赖之间的关系。同时这里也做了保底逻辑,如果没有找到对应的hexo
依赖的路径,那么会有一个保底逻辑,直接使用hexo-cli
中内置的 Context来实例化 Hexo对象,你可以在hexo-cli/lib/context.js
中找到对应的实现。
我们先来看看假如使用hexo-cli
中的实例化 Hexo是如何做的
1 | // hexo-cli/lib/context.js |
其他的暂时都不太重要,我们直接省去,我们先来看这一段
1 | // hexo-cli/lib/context.js |
这一段赋值了 extend,里面为 console实例化了一个 ConsoleExtend
对象,为什么要看这个?
1 | // hexo-cli/lib/hexo.js |
上面这段逻辑注册了 hexo的命令,那么里面是如何实现的插件注册逻辑?
hexo-cli的插件式结构
1 | // hexo-cli/lib/console/index.js |
使用了 Hexo实例化的 extend字段,里面的 console就是 ConsoleExtend
实例化的对象,使用 console.register
来为hexo控制台提供命令注册,我们来看下ConsoleExtend
的具体实现。
1 | // hexo-cli/lib/extend/console.js |
可以看到这里最核心的部分其实就是为传进来的回调方法添加上 options
和 desc
属性,因为fn传递的是引用,因此这里是会为传进来的fn修改对应的 options
和 desc
的。hexo的命令存储结构是类似 { [name]: fn: Function }
这种结构的(虽然我个人觉得这种方式不太好,更理想的结构应该是{ [name]: { fn: Function, desc: string } }
,类似这样的结构)。不过这不重要,知道整体的设计思路即可。
所有调用register
的都会被记录到 Hexo实例对象的 store字段中,这样就完成了插件的注册步骤,最终结构类似下面
1 | { |
如何执行 hexo ?
1 | function entry(cwd = process.cwd(), args) { |
我们依然只看关键的代码逻辑,可以看到hexo.call(cmd, args)
这句代码就是执行的关键,里面的cmd
参数代表了想要执行的命令,然后我们来看看hexo.call()
的实现。
1 | class Context extends EventEmitter { |
就是直接调用我们上文中register
注册的方法,然后根据传入的name执行对应的方法回调。hexo-cli
提供了init
、help
、version
三个命令(不包含hexo依赖中注册的),你可以去hexo-cli/lib/console
中去查看对应的三个命令实现,这里不再赘述,我们只提供主干流程的梳理,对应的细节你可以去代码中查看。
hexo-cli的执行过程
至此,hexo-cli的整体实现逻辑,我们已经看完了。画一张流程图简单梳理一下。
小结
整个cli的过程其实大同小异,和其他的cli整体实现思路比较类似。
- 感谢你的欣赏!