天天看點

Hexo+NexT(六):手把手教你編寫一個Hexo過濾器插件1. Typora的圖檔和NexT的資源檔案的統一2. 解決思路

Hexo+NexT(六):手把手教你編寫一個Hexo過濾器插件1. Typora的圖檔和NexT的資源檔案的統一2. 解決思路

歡迎通路部落客部落格www.guide2it.com

Hexo

+

NexT

介紹到這裡,我認為已經可以很好地完成任務了。它所提供的一些基礎功能及配置,都已經進行了講解。你已經可以随心所欲地配置一個自己的部落格環境,然後享受碼字的樂趣。

把部落格托管到Github上,是個很好的想法,沒有自己空間的部落客肯定很歡迎。其實文章編譯之後,他就是一個非常簡單的靜态網站。部署的目的就是簡單的把靜态網站檔案夾拷貝到Github的一個倉庫裡,然後把這個倉庫當作一個網站檔案夾,僅此而已,非常簡單。是以,沒有講的價值。

但是,作為一個

Coder

,研究了

Hexo

,總得來點真本事,提出一個方案,解決一個痛點,然後實作它。

痛點當然有,每次用

Typora

碼文章,習慣對文中圖檔所見即所得,無奈,

Typora

對圖檔的處理方式,

Hexo

不認可,轉換之後

url

錯亂,無法識别。是以,我希望

Typora

Hexo

用統一的方式處理圖檔,在

Typora

中和

Hexo

編譯之後都可以正常顯示。

沒有人解決,我就想解決它。

Hexo部落格專題索引頁

1. Typora的圖檔和NexT的資源檔案的統一

Typora

中,圖檔可以采用相對位置儲存,并且可以用文章檔案名進行靈活定制。如果我們在

Typora

中,把圖檔的儲存位置指定為與文章同名的檔案夾,那麼跟

NexT

提供的資源檔案夾就不謀而合了。

Typora

中,把圖檔的存儲位置設定為

./${filename}

,見圖。

Hexo+NexT(六):手把手教你編寫一個Hexo過濾器插件1. Typora的圖檔和NexT的資源檔案的統一2. 解決思路

NexT

的主題配置檔案中,打開資源檔案夾功能,

Hexo

編譯時會把資源檔案夾下的資源對象,根據引用它的頁面而賦予相應的

url

如果,我們把這兩者統一起來,在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,突然發現,其中的擴充是這樣的。

Hexo+NexT(六):手把手教你編寫一個Hexo過濾器插件1. Typora的圖檔和NexT的資源檔案的統一2. 解決思路

基本上所有的擴充都能夠望文生義,最有可能入手的地方就是

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

來嘗試一下。

2.3 編寫一個過濾器

找一個例子學習一下

https://hexo.io/plugins/

裡面找一個簡單的過濾器例子,發現它就是一個特别簡單的

Node

的包。比如過濾器插件[hexo-filter-auto-spacing](mmhy,它的檔案清單如下:

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

其中有用的也就是

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插件
下面我要加入一張圖檔了。
![測試](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

,它的内容及結構如上所示。跟内容相關的主要有三個字段

_content

content

raw

raw

表示原始文章,

_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

去掉。

Hexo+NexT(六):手把手教你編寫一個Hexo過濾器插件1. Typora的圖檔和NexT的資源檔案的統一2. 解決思路

繼續閱讀