這陣子心血來潮花了點時間整理敝部落格的原始碼,整理的過程發現其實可以把一些小工具獨立成模組,一方面可以讓 codebase 變精簡,另一方面則是抽出來的模組也可以給其他 Hexo 使用者使用。
一開始我以為只是搬移一下程式、剪剪貼貼就可以完成,後來越做越不對勁…原來要做一個 Hexo Plugin 也是滿多地方需要注意的。
最後的成品叫做 hexo-tag-photozoom,有興趣歡迎用用看~
History of this Feature
我這次要搬移的是我原本實作在 theme 裡面的功能,就是可以把內文圖片放大的功能,非常像 Medium 網站上按圖片會有的效果。效果如下:
原本我是直接在 theme 中使用 @nishanths 的 zoom.js,直接引用他的 sciprt,並自己註冊一個 Hexo tag:
1 | <!-- in theme layout file include external resource --> |
1 | /** |
透過 hexo.extend.tag.register
可以註冊新的 tag 語法,可以直接在文章 markdown 中使用,這個 tag 本身把 {% zoom %}
轉換成完整的 html 格式,並且由於已經在前端引用 zoom.js
library,所以就可以正常運作。
Move to Seperate Module
原本的作法是直接在 layout 中引用 zoom.{js,css}
library,這當然可行,但當要把這功能模組化時,是沒辦法直接接觸 layout 的 (除非你要在 readme 裡面叫使用者自己引用…),所以必須要有個方式把這些必要的外部資源塞進去使用者的 html 裡面。
關於這段「如何把外部資源塞到使用者的靜態檔中」,我參考了其他 plugin 的做法,發現大部份都是使用 hexo.extend.generator
來達成。不過我最後選擇其他做法來完成這件事。
Use Hexo Generator
Hexo 在編譯資源時,提供多種方式註冊自己的程式,來達到高度客製化。
其中 Generator
是用來產生檔案對應的路由,所以 Generator
都是回傳 { path: 'foo', data: 'foo' }
的格式,代表著 path 對應的 data 是什麼。
透過 Generator
可以做到 copy file 的功能,官方網站也有提供範例,再搭配註冊 tag ,就可以達成動態插入必要的外部資源。
1 | // generator that create a virtual path to external file |
如此一來,每當使用者插入 {% zoom %}
時,就會被展開成包含 include 外部資源的 html code,來達成目的。
Use Hexo Filter Inject Code
然而剛剛的方式有些小缺點,比方說當使用者插入很多 {% zoom %}
的 tag 時,就會出現很多重複引用的程式碼,感覺也是怪怪的。
所以我最後利用另一種方式達到塞 code 的效果 - Filter
。
Hexo Filter 提供很多 hook 的註冊點,比方說在渲染 html 之前執行註冊的 function …等等。
我這邊用的是 after_generate
,就是在全部檔案產生完成之後執行,詳細 hook 名稱跟意義可參考文件
透過 after_generate filter
我可以在最後決定是否要插入外部資源 zoom.{js,css}
,實作如下:
1 | hexo.extend.filter.register('after_generate', () => { |
我去掃所有 html 檔案,並搜尋有沒有 div
class name 是 photozoom
的,如果有那就直接在 html body 插入所需的 javascript 跟 css 程式碼,非常暴力但還不錯~
且這作法同時兼顧如果有使用者想在非文章內容的地方使用 zoom.js
的效果,只需要在 <img>
中加上 photozoom
class name,在每次編譯時都會掃到並在需要的地方插入程式碼。