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
![img](postname/sample.jpg) => {%asset_img sample.jpg%}
那就幸福了:在Typora
下采用![img](postname/sample.jpg)
使用圖片,享受所見即所得,在編譯過程當中轉化爲資源文件,自動得到,正確的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插件 下面我要加入一張圖片了。 ![測試](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
去掉。