Hexo+NexT(六):手把手教你编写一个Hexo过滤器插件

Hexo+NexT介绍到这里,我认为已经可以很好地完成任务了。它所提供的一些基础功能及配置,都已经进行了讲解。你已经可以随心所欲地配置一个自己的博客环境,然后享受码字的乐趣。

把博客托管到Github上,是个很好的想法,没有自己空间的博主肯定很欢迎。其实文章编译之后,他就是一个非常简单的静态网站。部署的目的就是简单的把静态网站文件夹拷贝到Github的一个仓库里,然后把这个仓库当作一个网站文件夹,仅此而已,非常简单。所以,没有讲的价值。

但是,作为一个Coder,研究了Hexo,总得来点真本事,提出一个方案,解决一个痛点,然后实现它。

痛点当然有,每次用Typora码文章,习惯对文中图片所见即所得,无奈,Typora对图片的处理方式,Hexo不认可,转换之后url错乱,无法识别。所以,我希望TyporaHexo用统一的方式处理图片,在Typora中和Hexo编译之后都可以正常显示。

没有人解决,我就想解决它。

Hexo博客专题索引页

1. Typora的图片和NexT的资源文件的统一

Typora中,图片可以采用相对位置保存,并且可以用文章文件名进行灵活定制。如果我们在Typora中,把图片的保存位置指定为与文章同名的文件夹,那么跟NexT提供的资源文件夹就不谋而合了。

Typora中,把图片的存储位置设置为./${filename},见图。

NexT的主题配置文件中,打开资源文件夹功能,Hexo编译时会把资源文件夹下的资源对象,根据引用它的页面而赋予相应的url

post_asset_folder: true

如果,我们把这两者统一起来,在markdown文章中我们能够在文章编译为html之前,实现这样的转换

![img](postname/sample.jpg) => {%asset_img sample.jpg%}

那就幸福了:在Typora下采用![img](postname/sample.jpg)使用图片,享受所见即所得,在编译过程中转化为资源文件,自动获得,正确的url,鱼与熊掌兼得,完美。

2. 解决思路

2.1 了解Hexo运作模式

研究Hexo的项目结构,主要研究页面的编译过程,也就是Hexo g命令是如何执行的。

根据Hexo的概述,Hexo项目的执行过程如下:

  1. 初始化
  2. 载入文件
  3. 执行指令
  4. 结束

第一步:初始化

初始化阶段,会创建Hexo实例,各种配置,各种插件,各种扩展全部就位,就等待载入文章进行处理。

Hexo通过项目包管理文件package.json引入各种插件扩展。

第二步:载入文件

载入source下所有的文章及样式、脚本等资源。如有指令,则可以监控该文件下面文件的变化。

第三步:执行指令

执行控制台指令,根据指令执行相应的命令。

第四步:退出

2.2 着手点

需要达成的目的,主要在编译页面的过程中,也就是主要在渲染render阶段。

Hexo的源代码中固然可以找到蛛丝马迹,但是这太麻烦了,速度也不快。有没有其他的方式。

换换思路,研究下Hexo提供的API,突然发现,其中的扩展是这样的。

基本上所有的扩展都能够望文生义,最有可能入手的地方就是Filter过滤器。

把它的定义摆上来:

hexo.extend.filter.register(type, function(data){
}, priority);
  • type是类型,表示过滤器的类型,过滤器的类型是什么意思?好吧,看看有什么类型

    before_post_renderafter_post_renderbefore_exitbefore_generate,这就是过滤器的插入时机啊。

  • function(data)是回调函数,这个很好地理解,其中的data是什么,回头再说。
  • prioritytype是过滤器的插入时机,如果在同一时机插入多个过滤器,那么就由priority来决定执行先后顺序,`priority值小就先执行。

重点在render

在上面的过滤器类型(就是过滤器的插入点)中,有一个重要的类型是before_post_render,意思就是在渲染之前执行过滤器。查一下Hexo的API,渲染的过程如下:

  • 执行 before_post_render 过滤器
  • 使用 Markdown 或其他渲染器渲染(根据扩展名而定)
  • 使用 Nunjucks 渲染
  • 执行 after_post_render 过滤器

好啊,那么我们拿before_post_render来尝试一下。

2.3 编写一个过滤器

找一个例子学习一下

https://hexo.io/plugins/里面找一个简单的过滤器例子,发现它就是一个特别简单的Node的包。比如过滤器插件hexo-filter-auto-spacingmmhy,它的文件清单如下:

  • lib

    • renderer.js
  • README.md
  • index.js
  • package.json

其中有用的也就是package.jsonindex.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插件
下面我要加入一张图片了。
![测试](2019-04-19-01测试插件/guide2it.jpg)

然后我编写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![测试](2019-04-19-01测试插件/guide2it.jpg)',
  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![测试](2019-04-19-01测试插件/guide2it.jpg)',
  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![测试](2019-04-19-01测试插件/guide2it.jpg)',
  site: { data: {} } }

原来这个data是一个Document,它的内容及结构如上所示。跟内容相关的主要有三个字段_contentcontentrawraw表示原始文章,_content这种带前缀_的一般是内部属性,不能动,那么就动content的内容。

按照资源对象的格式要求,应该把

![测试](2019-04-19-01测试插件/guide2it.jpg)转换为

{%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去掉。

原文地址:https://www.cnblogs.com/guide2it/p/11111715.html

时间: 2024-08-28 20:00:00

Hexo+NexT(六):手把手教你编写一个Hexo过滤器插件的相关文章

手把手教你编写一个简单的PHP模块形态的后门

看到Freebuf 小编发表的用这个隐藏于PHP模块中的rootkit,就能持久接管服务器文章,很感兴趣,苦无作者没留下PoC,自己研究一番,有了此文 0×00. 引言 PHP是一个非常流行的web server端的script语言.目前很多web应用程序都基于php语言实现.由于php是个开源软件并易于扩展,所以我们可以通过编写一个PHP模块(module 或者叫扩展 extension)来实现一个Backdoor. 本文就简单介下如何一步步编写一个简单的php 动态扩展后门. 0×01. p

[swift实战入门]手把手教你编写2048(三)

上篇地址:swift实战入门之手把手教你编写2048(二) github地址:https://github.com/scarlettbai/2048.git. 今天给大家带来2048最后一篇,之前已经实现了向游戏区域中随机插入数字块,接下来要做的,就是当我们滑动屏幕时移动及合并数字块以及插入一个新的数字块.本篇的难点就是移动时的算法问题,首先来给大家讲一下算法. 2048的算法实现其实很简单,假如我们当前数字格的格式如下: | |4| | | | | |4| | |2| |2|2| |2| |

手把手教你打造一个Material Design风格的App(一)

你应该听说过Android的Material Design,它是在Android 5.0(Lollipop)版本引入的.在Material Design中还引入了很多新东西,比如Material Theme,新的小部件,自定义的阴影,矢量图片及自定义动画等.如果你之前没有用过Material Design,那么本文将是一个很好的入门教程. 在这篇教程中,我们将会学习Material Design开发的基本步骤,即编写自定义的主题以及使用RecyclerView来实现抽屉导航. 通过下面的两个链接

[swift实战入门]手把手教你编写2048(二)

上篇地址:swift实战入门之手把手教你编写2048(一) github地址:https://github.com/scarlettbai/2048.git. 上篇文章已经中已经把2048的游戏区块画好了,这篇来加入计分板以及往游戏面板中插入数字块 计分板同样作为一个view,我们新建一个ScoreView.swift文件,代码如下: import UIKit //这里协议的作用是方便别的类中调用计分板的scoreChanged方法 protocol ScoreProtocol{ func sc

手把手教你打造一个Material Design风格的App(二)

--接上文. 3.1添加ToolBar(ActionBar) 添加ToolBar非常简单,你需要做的仅仅是为toolbar创建一个单独的layout布局,如果你想在哪里展示toolbar,只要在对应布局里将toolbar的布局文件include进来即可. (8)在res-->layout文件夹下创建一个名为toolbar.xml的文件,然后在里面添加一个android.support.v7.widget.Toolbar元素,这样就创建了一个具有特定高度和主题的toolbar. toolbar.x

手把手教你打造一个Material Design风格的App(三)

--接上文. 3.2添加抽屉导航 添加导航抽屉跟Android 5.0之前是一样的,只是以前我们使用ListView来作为菜单容器,现在我们则使用Material Design风格的RecyclerView. (14)在你工程的java文件夹中,创建3个名为activity.adapter.model的包,将MainActivity.java移到activtiy包中,这样做使得你的代码可以很好地组织和管理. (15)打开位于app模块下的build.gradle文件并添加如下依赖.添加完依赖之后

【手把手教你Eclipse插件开发】之插件入门

最近由于特殊需要,开始学习插件开发. 以前接触java也算有些时日,但是一直不知道有插件开发这样一种技术路径,本想着这开发工具都给你备好了,直接用不就行了.但是总有些IT工厂,为了要节约成本,开发自己的开发工具,但是又要节省时间,总不能一切都自己来.毕竟开发一个eclipse也要很长时间的.因此,插件开发出现在历史舞台. 首先要了解插件开发,就得从SWT/JFACE说起了.SWT是一种开源的界面开发框架,以前学java的时候,总是用一些panel,就类似这个.JFace又是一种基于SWT的UI不

[swift实战入门]手把手教你编写2048(一)

苹果设备越来越普及,拿着个手机就想捣鼓点啥,于是乎就有了这个系列,会一步一步教大家学习swift编程,学会自己做一个自己的app,github地址:https://github.com/scarlettbai/2048.git. 这篇文章需要大家了解一些swift基本语法,这里注重实践,就不讲太多基本语法了,不懂的大家可以Google一下,swift开发环境也很简单,直接在mac上安装一个XCode即可,首先我们来看下最终我们要实现的效果: 当然你也可以将其中的数字换成文字给你女票安手机上,还可

手把手教你画一个 逼格满满圆形水波纹loadingview Android

才没有完结呢o( ̄︶ ̄)n .大家好,这里是番外篇. 拜读了爱哥的博客,又学到不少东西.爱哥曾经说过: 要站在巨人的丁丁上. 那么今天,我们就站在爱哥的丁丁上来学习制作一款自定义view(开个玩笑,爱哥看到别打我). 转载请注明出处:http://blog.csdn.net/wingichoy/article/details/50523713 上一篇 带领大家做了一款炫酷的loading动画view 手把手带你做一个超炫酷loading成功动画view  不知道大家跟着做了一遍没有呢? 在开始之