Hexo
+NexT
介绍到这里,我认为已经能够很好地完成任务了。它所提供的一些基础功能及配置,都已经进行了讲解。你已经能够为所欲为地配置一个本身的博客环境,而后享受码字的乐趣。javascript
把博客托管到Github上,是个很好的想法,没有本身空间的博主确定很欢迎。其实文章编译以后,他就是一个很是简单的静态网站。部署的目的就是简单的把静态网站文件夹拷贝到Github的一个仓库里,而后把这个仓库看成一个网站文件夹,仅此而已,很是简单。因此,没有讲的价值。css
可是,做为一个Coder
,研究了Hexo
,总得来点真本事,提出一个方案,解决一个痛点,而后实现它。html
痛点固然有,每次用Typora
码文章,习惯对文中图片所见即所得,无奈,Typora
对图片的处理方式,Hexo
不承认,转换以后url
错乱,没法识别。因此,我但愿Typora
和Hexo
用统一的方式处理图片,在Typora
中和Hexo
编译以后均可以正常显示。java
没有人解决,我就想解决它。node
Hexo博客专题索引页git
在Typora
中,图片能够采用相对位置保存,而且能够用文章文件名进行灵活定制。若是咱们在Typora
中,把图片的保存位置指定为与文章同名的文件夹,那么跟NexT
提供的资源文件夹就不谋而合了。github
在Typora
中,把图片的存储位置设置为./${filename}
,见图。正则表达式
在NexT
的主题配置文件中,打开资源文件夹功能,Hexo
编译时会把资源文件夹下的资源对象,根据引用它的页面而赋予相应的url
。npm
post_asset_folder: true
若是,咱们把这二者统一块儿来,在markdown文章中咱们可以在文章编译为html以前,实现这样的转换json
 => {%asset_img sample.jpg%}
那就幸福了:在Typora
下采用
使用图片,享受所见即所得,在编译过程当中转化为资源文件,自动得到,正确的url
,鱼与熊掌兼得,完美。
研究Hexo
的项目结构,主要研究页面的编译过程,也就是Hexo g
命令是如何执行的。
根据Hexo
的概述,Hexo
项目的执行过程以下:
第一步:初始化
初始化阶段,会建立Hexo
实例,各类配置,各类插件,各类扩展所有就位,就等待载入文章进行处理。
Hexo
经过项目包管理文件package.json
引入各类插件扩展。
第二步:载入文件
载入source
下全部的文章及样式、脚本等资源。若有指令,则能够监控该文件下面文件的变化。
第三步:执行指令
执行控制台指令,根据指令执行相应的命令。
第四步:退出
须要达成的目的,主要在编译页面的过程当中,也就是主要在渲染render
阶段。
从Hexo
的源代码中当然能够找到蛛丝马迹,可是这太麻烦了,速度也不快。有没有其余的方式。
换换思路,研究下Hexo
提供的API,忽然发现,其中的扩展是这样的。
基本上全部的扩展都可以望文生义,最有可能入手的地方就是Filter
过滤器。
把它的定义摆上来:
hexo.extend.filter.register(type, function(data){ }, priority);
type
是类型,表示过滤器的类型,过滤器的类型是什么意思?好吧,看看有什么类型
before_post_render
、after_post_render
、before_exit
、before_generate
,这就是过滤器的插入时机啊。
function(data)
是回调函数,这个很好地理解,其中的data
是什么,回头再说。
priority
,type
是过滤器的插入时机,若是在同一时机插入多个过滤器,那么就由priority
来决定执行前后顺序,`priority值小就先执行。
重点在render
在上面的过滤器类型(就是过滤器的插入点)中,有一个重要的类型是before_post_render
,意思就是在渲染以前执行过滤器。查一下Hexo
的API,渲染的过程以下:
- 执行
before_post_render
过滤器- 使用 Markdown 或其余渲染器渲染(根据扩展名而定)
- 使用 Nunjucks 渲染
- 执行
after_post_render
过滤器
好啊,那么咱们拿before_post_render
来尝试一下。
找一个例子学习一下
从https://hexo.io/plugins/
里面找一个简单的过滤器例子,发现它就是一个特别简单的Node
的包。好比过滤器插件hexo-filter-auto-spacing
mmhy,它的文件清单以下:
其中有用的也就是package.json
和index.js
。而package.json
也就是典型的Node
包文件,它的输出对象由main
字段指定,本例中main
字段指向index
,也就是咱们的index.js
文件。
看一下index.js
内容
var assign = require('deep-assign'); var renderer = require('./lib/renderer'); hexo.extend.filter.register('before_post_render', renderer.render, 9);
再看一下/lib/renderer.js
的内容
var reg = /(\s*)(```) *(.*?) *\n?\[hide\]([\s\S]+?)\s*(\2)(\n+|$)/g; function ignore(data) { var source = data.source; var ext = source.substring(source.lastIndexOf('.')).toLowerCase(); return ['.js', '.css', '.html', '.htm'].indexOf(ext) > -1; } exports.render = function (data) { if (!ignore(data)) { data.content = data.content .replace(reg, function (raw, start, startQuote, lang, content, endQuote, end) { return start + end; }); } };
太简单了,对于上面这个例子,就是实现了过滤器的定义
hexo.extend.filter.register(type, function(data){ }, priority);
照猫画虎
与Hexo
项目文件并排新建一个文件node_modules
,并在里面新建项目hexo-image2asset
。结构以下:
├─guide2it-blog │ ├─node_modules │ ├─public │ ├─scaffolds │ ├─source │ │ ├─about │ │ │ └─index │ │ ├─categories │ │ ├─images │ │ ├─tags │ │ └─_posts │ │ ├─2019-04-19-01测试插件.md │ │ └─2019-04-19-01测试插件 │ │ └─guide2it.jpg │ ├─themes │ │ └─next └─node_modules └─hexo-image2asset ├─package.json └─index.js
至于为何要这样,这都是血的教训。对于Node
项目,新建模块应该在/guide2it-blog/node_modules
下面,我以前也是这样创建的,后来由于莫名奇妙的问题,采用万能的修复大法delete node_modules & npm install
以后,个人hexo-image2asset
项目找不到了,驾鹤西去了。
而我把hexo-image2asset
按上述方式布置,它也在Node项目的搜索路径上,也能够避免万能修复大法重蹈覆辙。
探究data的数据结构
为了弄清楚回调函数中data
的结构,我决定用一个例子来测试。
请看2019-04-19-01测试插件.md
的内容
--- 内容略 --- 测试hexo-image2asset插件 下面我要加入一张图片了。 
而后我编写index.js
,内容以下:
var deal_image=function(data){ console.log(data); } hexo.extend.filter.register('before_post_render', deal_image, 9);
执行hexo g
激发渲染过程。
Document { layout: 'post', title: '测试插件', date: moment("2019-03-05T09:00:00.000"), _content: '\n测试hexo-image2asset插件\n\n下面我要加入一张图片了。\n\n', source: '_posts/2019-04-19-01测试插件.md', raw: '---\nlayout: post\ntitle: \'测试插件\'\ndate: 2019/3/5 09:00:00\ncategory: [\'博客\',\'Hexo\']\ntags: [\'博客\',\'Hexo\',\'NexT\']\n---\n\n测试hexo-image2asset插件\n\n下面我要加入一张图片了。\n\n', slug: '01测试插件', published: true, updated: moment("2019-04-21T01:15:15.699"), comments: true, photos: [], link: '', _id: 'cjuprkojw0001o4d4cbawzsgo', path: [Getter], permalink: [Getter], full_source: [Getter], asset_dir: [Getter], tags: [Getter], categories: [Getter], content: '\n测试hexo-image2asset插件\n\n下面我要加入一张图片了。\n\n', site: { data: {} } }
原来这个data
是一个Document
,它的内容及结构如上所示。跟内容相关的主要有三个字段_content
、content
和raw
,raw
表示原始文章,_content
这种带前缀_
的通常是内部属性,不能动,那么就动content
的内容。
按照资源对象的格式要求,应该把

转换为
{%asset_img guide2it.jpg 测试%}
转换图片对象为资源对象
这个须要采用正则表达式来全局转换,被转换的字符串中有文章名字,这个须要首先找出来。
已知source形如_posts/2019-04-19-01测试插件.md
,那么文件名应该是,找到最右边的/
,其后的字符串,去掉.md
。
创建正则表达式来进行替换,把[]
内的内容用()
肯定为$1
,把图片文件名用()
定义为$2
,最终的正则表达式以下。
插件的index.js
完整内容以下。
var deal_image = function(data) { var reverseSource = data.source.split("").reverse().join(""); var fileName = reverseSource.substring(3, reverseSource.indexOf("/")).split("").reverse().join(""); var regExp = RegExp("!\\[([^\\f\\n\\r\\t\\v\\[\\]]+)\\]\\(" + fileName + '\\/([^\\\\\\/\\:\\*\\?\\"\\<\\>\\|\\,\\)]+)\\)'); data.content = data.content.replace(regExp, "{%asset_img $2 %}","g"); return data; } hexo.extend.filter.register('before_post_render', deal_image, 9);
这里有个bug,替换对象为"{%asset_img $2 $1 %}"
时,若是正则匹配的%1
是纯数字,则它被解释为图片宽度,这好像就离题了。因此暂时把$1
去掉。