Jekyll Hooks

使用 Hooks(钩子),你的插件可以对构建流程中的各个阶段进行更细粒度的控制。如果你的插件定义了任何 hook,Jekyll 会在预定义的时机调用它们。

Hooks 通过 owner(所有者)和 event name(事件名)进行注册。要注册一个 hook,需要调用 Jekyll::Hooks.register,并传入 hook owner、事件名称,以及在 hook 被触发时要执行的代码。

例如,如果你希望在 Jekyll 每次渲染页面后执行一些自定义逻辑,可以这样注册一个 hook:

Jekyll::Hooks.register :pages, :post_render do |page|
  # code to call after Jekyll renders a page
end

注意:下文提到的 :post_convert 事件是 v4.2.0 中引入的功能。

开箱即用的情况下,Jekyll 为所有者 :site:pages:documents:clean 预定义了 Hook 点。此外,为 :documents 定义的 Hook 点,也可以通过调用具体的集合类型来单独用于某个集合。也就是说,_posts 集合中的文档可使用 :posts_movies 集合中的文档可使用 :movies。在所有情况下,Jekyll 都会将所有者对象作为第一个回调参数传递给你的 Hook。

每个已注册的 Hook 所有者都支持以下事件 — :post_init:pre_render:post_convert:post_render:post_write — 不过,:site 所有者被设置为可以 响应 特殊事件名称。详情请参阅后续章节。

所有 :pre_render Hook 以及 :site, :post_render Hook 还会额外提供一个 payload 哈希作为第二个参数。在 :pre_render 事件中,payload 让你可以完全控制渲染期间可用的变量;而在 :site, :post_render 事件中,payload 则包含整个站点渲染完成后的最终值(适用于 sitemap、feed 等场景)。

内置 Hook Owners 和 Events

完整可用 hooks 列表如下:

Owner Event 触发时机

:site

表示整个网站

:after_init

网站初始化完成后立即触发。适合用于修改网站配置。每次 build / serve 会话只触发一次

:after_reset

网站在重新生成(regeneration)期间 reset 完成后立即触发

:post_read

所有源文件从磁盘读取并加载完成后触发

:pre_render

整个网站开始渲染之前触发

:post_render

整个网站渲染完成后、文件写入磁盘之前触发

:post_write

所有渲染后的文件写入磁盘后触发

:pages

允许对网站中的所有页面进行细粒度控制

:post_init

每当页面初始化时触发

:pre_render

页面开始渲染之前触发

:post_convert

页面内容转换完成后、页面 layout 渲染之前触发

:post_render

页面渲染完成后、写入磁盘之前触发

:post_write

页面写入磁盘后触发

:documents

允许对网站中的所有文档进行细粒度控制,包括文章和用户自定义集合中的文档

:post_init

每当任意文档初始化时触发

:pre_render

文档开始渲染之前触发

:post_convert

文档内容转换完成后、文档 layout 渲染之前触发

:post_render

文档渲染完成后、写入磁盘之前触发

:post_write

文档写入磁盘后触发

:posts

允许对网站中的所有文章进行细粒度控制,而不会影响用户自定义集合中的文档

:post_init

每当文章初始化时触发

:pre_render

文章开始渲染之前触发

:post_convert

文章内容转换完成后、文章 layout 渲染之前触发

文章内容转换完成后、文章 layout 渲染之前触发

:post_render

文章渲染完成后、写入磁盘之前触发

:post_write

文章写入磁盘后触发

:clean

用于在网站清理阶段,对被判定为过期并即将删除的文件列表进行细粒度控制。

:on_obsolete

在网站构建前,对目标目录进行清理时触发

自定义 Jekyll 对象的 Hooks

你也可以为你的插件引入的 Jekyll 对象注册并触发 Hook。你只需要在自定义类中合适的位置、使用合适的 owner 名称放置 trigger 调用,并在插件中注册该 owner 即可。

下面通过一个示例来说明。假设有这样一个插件,它会为每个初始化的自定义 Excerpt 对象实现自定义功能:

module Foobar
  class HookedExcerpt < Jekyll::Excerpt
    def initialize(doc)
      super
      trigger_hooks(:post_init)
    end

    def output
      @output ||= trigger_hooks(:post_render, renderer.run)
    end

    def renderer
      @renderer ||= Jekyll::Renderer.new(
        doc.site, self, site.site_payload
      )
    end

    def trigger_hooks(hook_name, *args)
      Jekyll::Hooks.trigger :excerpts, hook_name, self, *args
    end
  end
end

Jekyll::Hooks.register :excerpts, :post_init do |excerpt|
  Jekyll.logger.debug "Initialized:",
                      "Hooked Excerpt for #{excerpt.doc.inspect}"
end

Jekyll::Hooks.register :excerpts, :post_render do |excerpt, output|
  return output unless excerpt.doc.type == :posts
  Foobar.transform(output)
end