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

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 }} 分钟的阅读
加载中...
此文章数据所有权由区块链加密技术和智能合约保障仅归创作者所有。