Jekyll中文文档__Plugins

Jekyll有一个带有钩子的插件系统,允许您创建特定于您的网站的自定义生成内容。您可以为您的网站运行自定义代码,而无需修改 Jekyll 源代码本身。

注意:
您可以在 _config.yml 中的 whitelist “键” 中添加特定的插件,以允许它们在安全模式下运行。

Installation

Jekyll内置支持使用插件来扩展核心功能。

主要是,任何扩展名为 .rb 的文件都将在一个 build session 期间自动加载,该文件位于站点 source 的根目录的 _plugins 目录中。

此行为可以配置如下:

  • 可以通过命令行或配置文件直接更改 _plugins 目录。
  • 当 Jekyll 在 safe 模式下运行时,_plugins 目录中的插件(或其等效目录)将不会加载。
  • 该途径不能用于扩展 Jekyll CLI。

要使用打包为 gems 的插件,必须在名为 plugins 的顶级"键"下的配置文件中列出所需的 gems 。
此外,如果您是在 safe 模式下构建的,那么 gem 需要列在名为 whitelist 的顶级"键"下。例如:

plugins:
  - jekyll-gist
  - jekyll-coffeescript
  - jekyll-seo-tag
  - some-other-jekyll-plugin

# Enable safe mode
safe: true

# Whitelist plugins under safe mode.
# Note that `some-other-jekyll-plugin` is not listed here. Therefore,
# it will not be loaded under safe mode.
whitelist:
  - jekyll-gist
  - jekyll-coffeescript
  - jekyll-seo-tag

在没有 Gemfile 的情况下,必须手动确保在调用 Jekyll 之前已经安装了列出的插件。例如,上面列表中的 gems 的最新版本可以通过运行以下指令安装到系统范围的位置:

gem install jekyll-gist jekyll-coffeescript jekyll-remote-theme some-other-jekyll-plugin

Using a Gemfile

通过将 Gemfile(通常位于站点 source 的根目录)与名为 bundler 的 Rubygem 结合使用,可以大大简化各种 gem 依赖项的维护。然而,Gemfile 应该列出网站的所有主要依赖项,包括 Jekyll 本身,而不仅仅是网站的 gem-based 插件,因为 Bundler 将已安装的 gems 的范围缩小到仅仅是"运行时依赖项"(通过评估 Gemfile 来解决)。例如:

source "https://rubygems.org"

# Use the latest version.
gem "jekyll"

# The theme of current site, locked to a certain version.
gem "minima", "2.4.1"

# Plugins of this site loaded during a build with proper
# site configuration.
gem "jekyll-gist"
gem "jekyll-coffeescript"
gem "jekyll-seo-tag", "~> 1.5"
gem "some-other-jekyll-plugin"

# A dependency of a custom-plugin inside `_plugins` directory.
gem "nokogiri", "~> 1.11"

Gemfile 中列出的 gems 可以通过简单地运行 bundle install 来共同安装。

The :jekyll_plugins Gemfile group

Jekyll 对 Gemfile 中作为 :jekyll_plugins "组"的一部分而列出的 gems 进行特殊处理。该组下的任何 gem 都是在任何 Jekyll 进程的一开始加载的,而不考虑 --safe CLI flag 或配置文件中的条目。

虽然此途径允许使用额外的子命令和选项来增强 Jekyll 的 CLI ,或者避免在配置文件中列出 gems ,但缺点是需要注意"组"中包含哪些 gems 。例如:

source "https://rubygems.org"

# Use the latest version.
gem "jekyll"

# The theme of current site, locked to a certain version.
gem "minima", "2.4.1"

# Plugins of this site loaded only if configured correctly.
gem "jekyll-gist"
gem "jekyll-coffeescript"

# Gems loaded irrespective of site configuration.
group :jekyll_plugins do
  gem "jekyll-cli-plus"
  gem "jekyll-seo-tag", "~> 1.5"
  gem "some-other-jekyll-plugin"
end

Plugins on GitHub Pages

GitHub Pages 由 Jekyll 提供支持。所有 GitHub Pages 网站都是使用 --safe 选项生成的,以出于安全原因禁用插件(一些白名单插件除外)。不幸的是,这意味着如果你通过 GitHub Pages 进行部署,你的插件将无法工作。

你仍然可以使用 GitHub Pages 发布你的网站,但你需要在本地构建网站,并将生成的文件推送到你的GitHub存储库,而不是Jekyll 源文件。

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

如果您愿意,您可以在同一个站点同时使用上述任何插件渠道。其中一个的使用并不限制其他的使用。

Your first plugin

插件允许您扩展 Jekyll 的行为以满足您的需求。Jekyll 中有六种类型的插件。

Generators

Generators 在您的网站上创建内容。例如:

Converters

Converters 将标记语言更改为另一种格式。例如:

Commands

Commands 使用子命令扩展 jekyll 可执行文件。例如:

  • jekyll-compose 添加用于创建文章、页面或草稿的子命令。

Tags

Tags 创建自定义 Liquid tags. 例如:

Filters

Filters create custom Liquid filters. For example:

Hooks

Hooks 提供细粒度的控制来扩展构建过程。例如:

Flags

编写插件时需要注意两个标志:

FlagDescription

safe

一个布尔标志,用于通知 Jekyll 此插件是否可以在不允许执行任意代码的环境中安全执行。GitHub Pages使用它来确定哪些核心插件可以使用,哪些运行不安全。如果您的插件不允许执行任意代码,请将其设置为true 。GitHub Pages 仍然不会加载你的插件,但如果你提交它以包含在 core 中,最好是正确的!

priority

此标志决定插件的加载顺序。 有效的值为: :lowest, :low, :normal, :high, and :highest. 优先级最高的匹配将首先被应用,优先级最低的匹配将最后被应用。

要使用上面的一个示例插件作为示例,以下是您应该如何指定这两个标志:

module Jekyll
  class UpcaseConverter < Converter
    safe true
    priority :low
    ...
  end
end

Best Practices

这些指南帮助您了解创建插件的细节。我们还推荐了一些最佳实践来帮助构建您的插件。

我们建议您的插件使用 gem 。这将帮助您管理依赖关系,与站点源代码保持分离,并允许您在多个项目之间共享功能。有关创建 gem 的小贴士,请参阅 Ruby gems guide 或查看现有插件(如 jekyll-feed )的源代码。

Generators

当您需要Jekyll根据您自己的规则创建其他内容时,您可以创建一个生成器。

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

在 Jekyll 对现有内容进行盘点后,在网站生成之前,生成器会运行。有 front matter 的页面被存储为 Jekyll::Page 的实例,可通过 site.pages 获取。静态文件成为 Jekyll::StaticFile 的实例,并可通过 site.static_files 获取。有关详细信息,请参阅the Variables documentation pageJekyll::Site

在以下示例中,生成器将注入"在构建时为模板变量计算"的值。名为 reading.html 的模板有两个未定义变量 ongoingdone ,它们将在生成器运行时被定义或分配值:

module Reading
  class Generator < Jekyll::Generator
    def generate(site)
      book_data = site.data['books']
      ongoing = book_data.select { |book| book['status'] == 'ongoing' }
      done = book_data.select { |book| book['status'] == 'finished' }

      # get template
      reading = site.pages.find { |page| page.name == 'reading.html'}

      # inject data into template
      reading.data['ongoing'] = ongoing
      reading.data['done'] = done
    end
  end
end

以下示例是一个更复杂的生成器,用于生成新页面。

在本例中,生成器的目的是为 site 中注册的每个 category 创建一个页面。页面是在运行时创建的,因此它们的内容、front matter 和其他属性需要由插件本身设计。

  • 这些页面旨在渲染给定 category 下的所有文档的列表。因此,渲染文件的基本名称最好是 index.html
  • 拥有通过 front matter defaults 去配置页面的能力将是非常棒的!因此,为这些页面指定一个特定的 type 将是有益的。
module SamplePlugin
  class CategoryPageGenerator < Jekyll::Generator
    safe true

    def generate(site)
      site.categories.each do |category, posts|
        site.pages << CategoryPage.new(site, category, posts)
      end
    end
  end

  # Subclass of `Jekyll::Page` with custom method definitions.
  class CategoryPage < Jekyll::Page
    def initialize(site, category, posts)
      @site = site             # the current site instance.
      @base = site.source      # path to the source directory.
      @dir  = category         # the directory the page will reside in.

      # All pages have the same filename, so define attributes straight away.
      @basename = 'index'      # filename without the extension.
      @ext      = '.html'      # the extension.
      @name     = 'index.html' # basically @basename + @ext.

      # Initialize data hash with a key pointing to all posts under current category.
      # This allows accessing the list in a template via `page.linked_docs`.
      @data = {
        'linked_docs' => posts
      }

      # Look up front matter defaults scoped to type `categories`, if given key
      # doesn't exist in the `data` hash.
      data.default_proc = proc do |_, key|
        site.frontmatter_defaults.find(relative_path, :categories, key)
      end
    end

    # Placeholders that are used in constructing page URL.
    def url_placeholders
      {
        :path       => @dir,
        :category   => @dir,
        :basename   => basename,
        :output_ext => output_ext,
      }
    end
  end
end

生成的页面现在可以被设置为使用 destination 目录中特定路径的"特定布局或输出",所有这些都是通过"使用 front matter defaults" 的配置文件实现的。例如:

# _config.yml

defaults:
  - scope:
      type: categories  # select all category pages
    values:
      layout: category_page
      permalink: categories/:category/

Technical Aspects

Generators 只需要实施一种方法

MethodDescription

generate

Generates content as a side-effect.

如果生成器包含在单一文件中,则可以根据需要对其进行命名,但扩展名应为 .rb 。如果您的生成器被拆分到多个文件中,则应该将其打包为Rubygem,以便在 https://rubygems.org/ 发布。在这种情况下,gem 的名称取决于该站点名称的可用性,因为不存在两个 gems 可以具有相同的名称。

默认情况下,Jekyll 在 _plugins 目录中查找生成器。但是,您可以通过将所需的名称分配给配置文件中的"键" plugins_dir 来更改默认目录。

Converters

如果您有一种新的标记语言要用于您的网站,您可以通过实现自己的转换器来包含它。Markdown 和 Textile 标记语言都是使用这种方法实现的。

Remember your Front Matter

Jekyll 只转换顶部有 YAML 头的文件,即使是使用插件添加的转换器。

下面是一个转换器,它将接收所有以 .upcase 结尾的文章,并使用 UpcaseConverter 进行处理:

module Jekyll
  class UpcaseConverter < Converter
    safe true
    priority :low

    def matches(ext)
      ext =~ /^\.upcase$/i
    end

    def output_ext(ext)
      ".html"
    end

    def convert(content)
      content.upcase
    end
  end
end

转换器应至少实现3种方法:

MethodDescription

matches

给定的扩展是否与此转换器的可接受扩展列表匹配?采用一个参数:文件的扩展名(包括那个点)。如果匹配,则必须返回true,否则必须返回false

output_ext

要指定给输出文件的扩展名(包括点)。通常这将是".html"

convert

进行"内容转换"的逻辑。采用一个参数:文件的原始内容(没有 front matter)。必须返回一个字符串。

在我们的示例中,UpcaseConverter#matches 会检查我们的文件扩展名是否为 .upcase ,如果是,则会使用转换器进行渲染。它会调用 UpcaseConverter#convert 来处理内容。在我们的简单转换器中,我们只是对整个内容字符串进行大写。最后,当它保存页面时,它将使用 .html 扩展名进行保存。

Commands

从 2.5.0 版本开始,Jekyll 可以通过插件进行扩展,这些插件为 jekyll 可执行文件提供子命令。这可以通过将相关插件包含在名为 :jekyll_pluginsGemfile 组中来实现:

group :jekyll_plugins do
  gem "my_fancy_jekyll_plugin"
end

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

class MyNewCommand < Jekyll::Command
  class << self
    def init_with_program(prog)
      prog.command(:new) do |c|
        c.syntax "new [options]"
        c.description 'Create a new Jekyll site.'

        c.option 'dest', '-d DEST', 'Where the site should go.'

        c.action do |args, options|
          Jekyll::Site.new_site_at(options['dest'])
        end
      end
    end
  end
end

命令应实现此"单类"方法:

MethodDescription
init_with_program 这个方法接受一个参数,Mercenary::Program实例,它就是 Jekyll 程序本身。在程序上,可以使用上述语法来创建命令。有关更多详细信息,请访问 GitHub.com 上的 Mercenary 仓库。

Tags

如果你想在你的网站中包含自定义的 liquid tags ,你可以通过连接 tagging 系统来实现。Jekyll 添加的内置示例包括highlightinclude tags 。以下是一个自定义 liquid tag 的示例,该 tag 将输出页面被渲染的时间:

module Jekyll
  class RenderTimeTag < Liquid::Tag

    def initialize(tag_name, text, tokens)
      super
      @text = text
    end

    def render(context)
      "#{@text} #{Time.now}"
    end
  end
end

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

liquid tags 至少必须实现:

MethodDescription

render

Outputs the content of the tag.

您还必须向 Liquid 模板引擎注册自定义 tag ,如下所示:

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

在上面的例子中,我们可以在其中一个页面的任何位置放置以下 tag :

<p>{% render_time page rendered at: %}</p>

我们会在页面上看到这样的内容:

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

Tag Blocks

上面看到的 render_time tag 也可以通过继承 Liquid::Block 类重写为 tag 块。看看下面的例子:

module Jekyll
  class RenderTimeTagBlock < Liquid::Block

    def render(context)
      text = super
      "<p>#{text} #{Time.now}</p>"
    end

  end
end

Liquid::Template.register_tag('render_time', Jekyll::RenderTimeTagBlock)

我们现在可以在任何地方使用 tag 块:

{% render_time %}
page rendered at:
{% endrender_time %}

我们仍然可以在页面上获得与上面相同的输出:

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

注意:
在上面的示例中,tag 块和 tag 都使用名称 render_time 注册,但不建议在同一项目中使用相同名称注册 tag 和 tag 块,因为这可能会导致冲突。

Filters

Filters 是一系列的模块,该模块用于将其方法导出为 liquid 。所有方法都必须采用至少一个"表示过滤器输入"的参数。返回值将是过滤器的输出。

module Jekyll
  module AssetFilter
    def asset_url(input)
      "http://www.example.com/#{input}?#{Time.now.to_i}"
    end
  end
end

Liquid::Template.register_filter(Jekyll::AssetFilter)

有关创建自定义 Liquid Filters 的更多详细信息,请访问 Liquid docs

ProTip™: Access the site object using Liquid

Jekyll 允许您通过 @context.registers[:site] 中 Liquid 的 @context.registers 功能访问 site 对象。例如,您可以使用@context.registers[:site].config 访问全局配置文件 _config.yml

Hooks

使用钩子,插件可以对构建过程的各个方面进行细粒度的控制。如果你的插件定义了任何钩子,Jekyll 会在预定义的点调用它们。

Hooks 被注册到所有者和事件名称。要注册一个,请调用 Jekyll::Hooks.register ,并传递钩子所有者、事件名称和代码,以便在触发钩子时调用。例如,如果你想在每次 Jekyll 渲染页面时执行一些自定义功能,你可以注册一个钩子,如下所示:

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

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

开箱即用,Jekyll为所有者预先定义了 hook 点::site, :pages, :documents:clean 。此外,为 :documents 定义的 hook 点只能通过调用集合类型来用于单个集合。即::posts 用于 collection _posts 中的文档和 :movies 用于 collection _movies 中的文档。在所有情况下,Jekyll 都会使用 owner 对象作为第一个回调参数来调用钩子。

每个注册的 hook 所有者都支持以下事件 - :post_init, :pre_render, :post_convert, :post_render, :post_write - 但是,:site 所有者被设置为响应"特殊的事件名称"。有关详细信息,请参阅下一节。

所有的 :pre_render 钩子和 :site, :post_render 钩子也将提供一个 payload hash 作为第二个参数。在 :pre_render 事件的情况下,payload 使您能够完全控制渲染期间可用的变量,而在 :site, :post_render 事件中,payload 包含渲染所有站点后的最终值(对 sitemaps, feeds 等有用)。

Built-in Hook Owners and Events

可用钩子的完整列表:

OwnerEventTriggered at

:site

Encompasses the entire site

:after_init

Just after the site initializes. Good for modifying the configuration of the site. Triggered once per build / serve session

:after_reset

Just after the site resets during regeneration

:post_read

After all source files have been read and loaded from disk

:pre_render

Just before rendering the whole site

:post_render

After rendering the whole site, but before writing any files

:post_write

After writing all of the rendered files to disk

:pages

Allows fine-grained control over all pages in the site

:post_init

Whenever a page is initialized

:pre_render

Just before rendering a page

:post_convert

After converting the page content, but before rendering the page layout

:post_render

After rendering a page, but before writing it to disk

:post_write

After writing a page to disk

:documents

Allows fine-grained control over all documents in the site including posts and documents in user-defined collections

:post_init

Whenever any document is initialized

:pre_render

Just before rendering a document

:post_convert

After converting the document content, but before rendering the document layout

:post_render

After rendering a document, but before writing it to disk

:post_write

After writing a document to disk

:posts

Allows fine-grained control over all posts in the site without affecting documents in user-defined collections

:post_init

Whenever a post is initialized

:pre_render

Just before rendering a post

:post_convert

After converting the post content, but before rendering the postlayout

:post_render

After rendering a post, but before writing it to disk

:post_write

After writing a post to disk

:clean

Fine-grained control on the list of obsolete files determined to be deleted during the site's cleanup phase.

:on_obsolete

During the cleanup of a site's destination before it is built

Hooks for custom Jekyll objects

您还可以为插件引入的 Jekyll 对象注册并触发钩子。所需要的就是将 trigger 调用放在一个合适的 owner 名称下,放在自定义类中所需的位置上,并通过插件注册 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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值