Soptq

Soptq

Probably a full-stack, mainly focusing on Distributed System / Consensus / Privacy-preserving Tech etc. Decentralization is a trend, privacy must be protected.
twitter
github
bilibili

從零開始實現自己的 Jekyll 插件:過濾器

Filter 插件是什麼?#

Filters 插件是 Jekyll 插件中最為常見的一種類型,它可以將一種 Ruby 的內部方法暴露給 Liquid ,最後在頁面呼叫。比如說,如果有閱讀使用 Jekyll 搭建的靜態網頁的話,我們可能會注意到很多形如下面的內容:

{% assign foo = 10 %}
{% foo | plus: 4 %}
{{ foo }}

這就是 Liquid 語言,第一行和第二行使用 {% %} 包著的是運算式,而第三行使用 {{ }} 包著的表示將抱著的變數輸出。Liquid 是一種很簡單的模板語言,半天就可以學完,對 Liquid 有更多興趣的可以去這個網站查看一下官方的文檔

所以說了這麼多,Filter 到底是什麼呢?其實在上面的代碼中的第二行,plus 就是一個 Filter,表示把 foo 作為輸入,把 foo 加 4 後重新賦值給 foo ,相當於 foo = foo + 4

Filter 有什麼用?#

Filter 實際上或許是 Jekyll 所允許的插件中第二有用的插件類型(第一有用的插件類型應該是 Hook),其受用面非常廣,包括但不限於:

  • 快捷計算兩個日期之間的差距。
  • 對文章生成目錄。
  • 自動混淆郵件地址。
  • 計算文章字數和閱讀時間。

為什麼我覺得這些都可以用 JS 實現?#

確實,理論上來說所有使用 Filter 插件可以做到的事情都可以使用 javascript 在運行時完成,但如果我們使用插件來使這些事情在編譯時就完成了的話,無疑可以增強網站在遊覽器上的性能,所以還是非常有用的。

注意!Pages 上無法使用插件#

GitHub Pages (或者其他大部分 Pages) 考慮到安全因素,都通過 —safe 選項禁用了插件功能。因此如果你的網站部署在 Github Pages ,那麼你的插件不會工作。

不過仍然有辦法發布到 GitHub Pages,你只需在本地做一些轉換,並把生成好的文件上傳到 Github 替代 Jekyll 就可以了。

讓我們開始吧#

本篇將從最簡單的 Filter 開始,實現一個簡單的計算文章字數和閱讀時間的 Filter。這個 Filter 可以幫助網頁在展示文章的同時展示字數和預計的閱讀時間。

根據 Jekyll 的官方文檔,所有的 Jekyll 插件都要存儲在項目目錄下的 _plugins 文件夾,所以如果項目根目錄下沒有 _plugins 文件夾的話需要先手動新建一個 _plugins 文件夾。然後我們在新建的 _plugins 文件夾中新建一個 Ruby 文件叫做 jekyll_reading_time.rb。然後打開這個文件,我們將我們的這個插件命名為 ReadingTime

module ReadingTime

end 

我們需要暴露給 Liquid 的方法有兩個。第一個是 count_words(html) 方法:傳入網頁,計算文章中的字數;第二個是 reading_time(html) 方法:傳入網頁,計算大概的閱讀時間。reading_time(html) 方法很好寫,我們只需要得到有多少字數,然後除以每分鐘的閱讀字數,向上取整就是閱讀時間了。

module ReadingTime

	def count_words(html)
		
	end

	def reading_time(html)
		(count_words(html) / 140.0).ceil
	end

end 

為了計算網頁的字數,我們可以首先使用 Ruby 庫 nokogiri來解析 html 為一個對象,然後遞歸每一個 html 對象,忽略一些肯定沒有文字的對象,對其他的對象查詢其中有沒有文字。我們使用私有函數來處理這個過程。

require ‘nokogiri’

module ReadingTime

	def count_words(html)
		words(html)
	end

	def reading_time(html)
		(count_words(html) / 140.0).ceil
	end

	private

	def text_nodes(root)
		ignored_tags = %w[ area audio canvas code embed footer form img map math nav object pre script svg table track video ]

		texts = 0
		root.children.each { |node|
			if node.text?
		    	text_array = node.text.unpack(“U*”)
		    	text_array.each { |c|
		        	texts = texts + 1
		    	}
			elsif not ignored_tags.include? node.name
				texts = texts + text_nodes(node)
			end
		}
		texts
	end

	def words(html)
		fragment = Nokogiri::HTML.fragment html
		text_nodes(fragment)
	end

end 

可以看到我們的插件整體已經成型了,只差最後一步了,就是去 Jekyll 註冊這個插件。

為了向 Jekyll 註冊我們的新插件,我們只需要在模塊外調用 Jekyll 的註冊方法。

require ‘nokogiri’

module ReadingTime

	def count_words(html)
		words(html)
	end

	def reading_time(html)
		(count_words(html) / 140.0).ceil
	end

	private

	def text_nodes(root)
		ignored_tags = %w[ area audio canvas code embed footer form img map math nav object pre script svg table track video ]

		texts = 0
		root.children.each { |node|
			if node.text?
		    	text_array = node.text.unpack(“U*”)
		    	text_array.each { |c|
		        	texts = texts + 1
		    	}
			elsif not ignored_tags.include? node.name
				texts = texts + text_nodes(node)
			end
		}
		texts
	end

	def words(html)
		fragment = Nokogiri::HTML.fragment html
		text_nodes(fragment)
	end

end 

Liquid::Template.register_filter(ReadingTime)

完成了!我們的第一個插件就這樣完成了!以後在編寫網頁的時候,如果想得到一篇文章的字數和估計的閱讀時間,只需要在 html 代碼中這樣寫就可以了:

這篇文章有 {{ post.content | count_words }} 字
大概需要 {{ post.content | reading_time }} 分鐘的閱讀
載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。