![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBnL3gTOzUzNwIjM5IjNwkTMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
歡迎通路部落客部落格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}
,見圖。
在
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
項目的執行過程如下:
- 初始化
- 載入檔案
- 執行指令
- 結束
第一步:初始化
初始化階段,會建立
Hexo
執行個體,各種配置,各種插件,各種擴充全部就位,就等待載入文章進行處理。
Hexo
通過項目包管理檔案
package.json
引入各種插件擴充。
第二步:載入檔案
載入
source
下所有的文章及樣式、腳本等資源。如有指令,則可以監控該檔案下面檔案的變化。
第三步:執行指令
執行控制台指令,根據指令執行相應的指令。
第四步:退出
2.2 着手點
需要達成的目的,主要在編譯頁面的過程中,也就是主要在渲染
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
來嘗試一下。
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
去掉。