Jekyll 支持插件功能,你可以很容易的加入自己的代码。

在 GitHub Pages 使用插件

GitHub Pages 是由 Jekyll 提供技术支持的,考虑到安全因素,所有的 Pages 通过 —safe 选项禁用了插件功能,因此如果你的网站部署在 Github Pages ,那么你的插件不会工作。

不过仍然有办法发布到 GitHub Pages,你只需在本地做一些转换,并把生成好的文件上传到 Github 替代 Jekyll 就可以了。

安装插件

可以使用以下三种方式安装插件:

  1. 在网站根下目录建立 _plugins 文件夹,插件放在这里即可。 Jekyll 运行之前,会加载此目录下所有以 *.rb 结尾的文件。
  2. 在你的_config.yml文件,添加一个键为gems的新数组,其值为你想要使用的gem插件的名字,例如:
  1. gems: [jekyll-coffeescript, jekyll-watch, jekyll-assets]
  2. # This will require each of these gems automatically.
  3. 然后安装插件使用命令 `gem install jekyll-coffeescript jekyll-watch jekyll-assets`
  1. Gemfile 中的一个Bundler组里添加相关的插件,例如:

    1. group :jekyll_plugins do
    2. gem "my-jekyll-plugin"
    3. gem "another-jekyll-plugin"
    4. end

    然后需要从Bundler组中安装所有插件,使用命令bundle install

_plugins, _config.yml and Gemfile 可以同时使用

如果你愿意,可以在网站中同时使用上述安装插件的方法,使用一种安装方法并不会影响别的方法生效。

通常,插件最终会被放在以下的目录中:

  1. 生成器 - Generators
  2. 转换器 - Converters
  3. 命令 - Commands
  4. 标记 - Tags
  5. 钩子 - Hooks

生成器 - Generators

当你需要Jekyll根据你自己的规则来添加额外的内容时,你可以创建一个生成器。

生成器是 Jekyll:Generator 的子类,定义了一个 generate 方法, 接收一个Jekyll::Site类型的实例, 方法的返回值会被忽略。

生成器在Jekyll生成现存内容详细目录之后,网站生成之前运行, 含有YAML头信息的页面储存为Jekyll::Page实例,可以通过site.pages变量获取, 静态文件储存为Jekyll::StaticFile实例,可以通过site.static_files变量获取, 详情见变量文档Jekyll::Site

比如,生成器可以为模板变量注入构建过程中计算出的变量值,在下面的例子中, 我们在生成器中为模板reading.html的两个变量ongoingdone赋了值:

  1. module Reading
  2. class Generator < Jekyll::Generator
  3. def generate(site)
  4. ongoing, done = Book.all.partition(&:ongoing?)
  5. reading = site.pages.detect {|page| page.name == 'reading.html'}
  6. reading.data['ongoing'] = ongoing
  7. reading.data['done'] = done
  8. end
  9. end
  10. end

下面是一个更复杂的生成器例子,可以生成新的页面:

  1. module Jekyll
  2. class CategoryPage < Page
  3. def initialize(site, base, dir, category)
  4. @site = site
  5. @base = base
  6. @dir = dir
  7. @name = 'index.html'
  8. self.process(@name)
  9. self.read_yaml(File.join(base, '_layouts'), 'category_index.html')
  10. self.data['category'] = category
  11. category_title_prefix = site.config['category_title_prefix'] || 'Category: '
  12. self.data['title'] = "#{category_title_prefix}#{category}"
  13. end
  14. end
  15. class CategoryPageGenerator < Generator
  16. safe true
  17. def generate(site)
  18. if site.layouts.key? 'category_index'
  19. dir = site.config['category_dir'] || 'categories'
  20. site.categories.keys.each do |category|
  21. site.pages << CategoryPage.new(site, site.source, File.join(dir, category), category)
  22. end
  23. end
  24. end
  25. end
  26. end

本例中,生成器在 categories 下生成了一系列文件。并使用布局 category_index.html 列出所有的文章。

生成器只需要实现一个方法:

Method Description

generate

String output of the content being generated.

转换器 - Converters

如果想使用一个新的标记语言,可以用你自己的转换器实现,Markdown 和 Textile 就是这样实现的。

记住你的 YAML 头信息

Jekyll 只会转换带有 YAML 头信息的文件,即使你使用了插件也不行。

下边的例子实现了一个转换器,他会用 UpcaseConverter 来转换所有以 .upcase 结尾的文件。

  1. module Jekyll
  2. class UpcaseConverter < Converter
  3. safe true
  4. priority :low
  5. def matches(ext)
  6. ext =~ /^\.upcase$/i
  7. end
  8. def output_ext(ext)
  9. ".html"
  10. end
  11. def convert(content)
  12. content.upcase
  13. end
  14. end
  15. end

转换器需要最少实现以下 3 个方法:

方法 描述

matches

检查文件后缀名是否是所要的,传入的参数是文件的后缀名(包括点号),接受的返回值是 truefalse

output_ext

生成文件的后缀名(包括点号),通常是 “.html”

convert

转换逻辑,传入原始文件内容(不包含YAML头信息),返回值需要是 String 。

在上边的例子中, UpcaseConverter#matches 检查文件后缀名是不是 .upcase ;UpcaseConverter#convert 会处理检查成功文件的内容,即将所有的字符串变成大写;最终,保存的结果将以作为后缀名 .html

命令 - Commands

从2.5.0版本开始,Jekyll可以使用插件提供的子命令来扩展jekyll命令。 可以通过在Gemfile:jekyll_plugins组中包括相关的插件来实现:

  1. group :jekyll_plugins do
  2. gem "my_fancy_jekyll_plugin"
  3. end

每个Command必须是Jekyll::Command类的子类,而且必须要包含类方法init_with_program,例如:

  1. class MyNewCommand < Jekyll::Command
  2. class << self
  3. def init_with_program(prog)
  4. prog.command(:new) do |c|
  5. c.syntax "new [options]"
  6. c.description 'Create a new Jekyll site.'
  7. c.option 'dest', '-d DEST', 'Where the site should go.'
  8. c.action do |args, options|
  9. Jekyll::Site.new_site_at(options['dest'])
  10. end
  11. end
  12. end
  13. end
  14. end

命令应该实现这个单例方法:

方法 描述

init_with_program

这个方法接受一个参数, Mercenary::Program 实例,表示Jekyll程序本身,程序之上,命令可以通过上述方式创建,详情可以参见Github.com上的Mercenary库。

标记 - Tags

如果你想使用 liquid 标记,你可以这样做。 Jekyll 官方的例子有 highlightinclude 等标记。下边的例子中,自定义了一个 liquid 标记,用来输出当前时间:

  1. module Jekyll
  2. class RenderTimeTag < Liquid::Tag
  3. def initialize(tag_name, text, tokens)
  4. super
  5. @text = text
  6. end
  7. def render(context)
  8. "#{@text} #{Time.now}"
  9. end
  10. end
  11. end
  12. Liquid::Template.register_tag('render_time', Jekyll::RenderTimeTag)

liquid 标记最少需要实现如下方法:

方法 描述

render

输出标记的内容。

你必须同时用 Liquid 模板引擎注册自定义标记,比如:

  1. Liquid::Template.register_tag('render_time', Jekyll::RenderTimeTag)

对于上边的例子,你可以把如下标记放在页面的任何位置:

  1. {% raw %}
  2. <p>{% render_time page rendered at: %}</p>
  3. {% endraw %}

我们在页面上会得到如下内容:

  1. <p>page rendered at: Tue June 22 23:38:47 –0500 2010</p>

Liquid 过滤器

你可以像上边那样在 Liquid 模板中加入自己的过滤器。过滤器会把自己的方法暴露给 liquid。所有的方法都必须至少接收一个参数,用来传输入内容;返回值是过滤的结果。

  1. module Jekyll
  2. module AssetFilter
  3. def asset_url(input)
  4. "http://www.example.com/#{input}?#{Time.now.to_i}"
  5. end
  6. end
  7. end
  8. Liquid::Template.register_filter(Jekyll::AssetFilter)
提示™:用 Liquid 访问 site 对象

Jekyll 允许通过 Liquid 的context.registers 特性来访问 site 对象。比如可以用 context.registers.config 访问配置文件 _config.yml

Flags

当写插件时,有两个标记需要注意:

标记 描述

safe

告诉 Jekyll 此插件是否可以安全的执行任意代码。 GitHub Pages 用他来决定那个插件可以使用,哪些不可以使用。如果你的插件不允许执行任意代码,把它设为 true 即可。 GitHub Pages 仍然不会加载你的插件,但是如果你把他夹杂到核心中,最后保证此值设置的正确!

priority

此标记决定加载插件的顺序。可以是这些值::lowest, :low, :normal, :high, 还有 :highest。优先级高的先执行,优先级低的后执行。

以上边例子的插件为例,应该这样设置这两个标记:

  1. module Jekyll
  2. class UpcaseConverter < Converter
  3. safe true
  4. priority :low
  5. ...
  6. end
  7. end

钩子 - Hooks

通过使用钩子,你的插件可以精细控制构建过程中的各个方面。 如果你的插件定义了钩子,Jekyll就会在预定义时调用钩子。

钩子需要注册一个容器名称和事件名称,可以使用 Jekyll::Hooks.register 来注册, 传递一个容器名称、事件名称,以及钩子触发时调用的代码。 比如,假设你想要在 Jekyll 每次渲染博客时执行一些自定义的功能代码,你可以这样注册钩子:

  1. Jekyll::Hooks.register :posts, :post_render do |post|
  2. # Jekyll 渲染博客后要调用的代码
  3. end

Jekyll 分别为 :site:pages:posts:documents 提供了钩子。 在任何情况下,Jekyll 都会以容器对象作为第一个回调参数来调用你的钩子, 但是所有的 :pre_render:site, :post_render钩子都会提供一个负载哈希值作为第二个参数。 如果是 :pre_render,负载可以让你完全掌控渲染过程中的变量。 若是 :site, :pre_render,负载包括渲染所有网站的最后值(可以用于站点地图,订阅等)

完整的钩子列表如下:

容器名 事件名 调用时机

:site

:after_init

在网站初始化时,但是在设置和渲染之前,适合用来修改网站的配置项

:site

:after_reset

网站重置之后

:site

:post_read

在网站数据从磁盘中读取并加载之后

:site

:pre_render

在渲染整个网站之前

:site

:post_render

在渲染整个网站之后,但是在写入任何文件之前

:site

:post_write

在将整个网站写入磁盘之后

:pages

:post_init

每次页面被初始化的时候

:pages

:pre_render

在渲染页面之前

:pages

:post_render

在页面渲染之后,但是在页面写入磁盘之前

:pages

:post_write

在页面写入磁盘之后

:posts

:post_init

每次博客被初始化的时候

:posts

:pre_render

在博客被渲染之前

:posts

:post_render

在博客渲染之后,但是在被写入磁盘之前

:posts

:post_write

在博客被写入磁盘之后

:documents

:post_init

每次文档被初始化的时候

:documents

:pre_render

在渲染文档之前

:documents

:post_render

在渲染文档之后,但是在被写入磁盘之前

:documents

:post_write

在文档被写入磁盘之后

可用的插件

下边的插件,你可以按需索取:

生成器

转换器

过滤器

标签

集合

其他

期待你的作品

如果你有一个 Jekyll 插件并且愿意加到这个列表中来,可以阅读此须知,并参照着来做。