Jekyll 构建内容的排序规则

29 January 2017 tomjoht tomjoht

Jekyll 的主要工作是将你的原始文本文件转换为一个静态网站。它通过在生成静态 HTML 输出时渲染 Liquid、Markdown 以及其他转换来实现这一点。

在这个转换过程中,理解 Jekyll 的解释顺序是非常重要的。所谓“解释顺序”,指的是在转换内容时,哪些内容会被渲染、渲染的先后顺序,以及所应用的规则。

如果某个元素没有被正确转换,你可以通过分析解释顺序来排查问题。

解释顺序

Jekyll 按照以下顺序转换你的网站:

  1. 站点变量
    Jekyll 会遍历你的文件并填充站点变量,例如 sitepagepost 和集合对象(collection objects)。通过这些对象,Jekyll 可以确定永久链接(permalinks)、标签(tags)、分类(categories)以及其他细节的值。

  2. Liquid
    Jekyll 会处理包含前置数据页面中的 Liquid 格式。你可以通过以下方式识别 Liquid:

    • Liquid 标签(tags){% 开始,以 %} 结束。例如:{% highlight %}{% seo %}。标签可以是块标签(block)或内联标签。块标签会有一个对应的结束标签,例如 {% endhighlight %}

    • Liquid 变量(variables) 以双大括号开始和结束。例如:{{ site.myvariable }}{{ content }}

    • Liquid 过滤器(filters) 以竖线符号(|)开头,只能用于 Liquid 变量 中变量字符串之后。例如:{{ "css/main.css" | relative_url }} 中的 relative_url 过滤器。

  3. Markdown
    Jekyll 使用你在配置文件中指定的 Markdown 解析器将 Markdown 转换为 HTML。文件必须具有 Markdown 扩展名并包含前置数据,Jekyll 才会对其进行转换。

  4. 布局(Layout)
    Jekyll 会将页面内容嵌入其前置数据中指定的布局(或配置文件中指定的布局)中。每个页面的内容会插入到布局中的 {{ content }} 标签里。

  5. 文件
    Jekyll 会将生成的内容写入 _site 目录中的目录结构中。页面、文章和集合会根据它们的永久链接设置进行组织。以 _ 开头的目录(例如 _includes_data)通常不会出现在输出文件中。

配置错误导致的问题场景

大多数情况下,在构建 Jekyll 网站时你不需要考虑解释顺序。只有当内容没有正确渲染时,这些细节才变得重要。

下面这些场景展示了一些你可能会遇到的问题。这些问题通常是因为误解了解释顺序,但也都很容易修复。

页面中的变量无法渲染,因为变量是在布局中赋值的

假设你在布局文件(_layouts/default.html)中设置了一个变量:

{% assign myvar = "joe" %}

然后在某个使用这个布局的页面中,你尝试引用这个变量:

{{ myvar }}

结果变量没有被渲染出来,这是因为 Jekyll 的解释顺序是先渲染页面中的 Liquid,再处理布局文件。当页面渲染 Liquid 时,布局中赋值的变量还不存在。

为了解决这个问题,你可以把变量的赋值放到页面的前置数据(front matter)中。

包含文件中的 Markdown 没有被处理

假设你在 _includes/mycontent.md 中有一个 Markdown 文件,其中包含一些 Markdown 格式:

This is a list:
* first item
* second item

你在一个 HTML 文件中通过如下方式引入这个文件:

{% include mycontent.md %}

这段 Markdown 不会被渲染,因为首先 Jekyll 处理的是 Liquid(include 标签),它会把 mycontent.md 的内容插入到 HTML 文件中。然后 才是 Markdown 的处理阶段。

但由于内容是被插入到一个 HTML 页面中的,Markdown 并不会被处理。Markdown 过滤器只会作用于 Markdown 文件。

为了让这段代码正确渲染,你应该在引入 HTML 文件的包含文件中使用 HTML 格式,而不是 Markdown。

需要注意的是,highlight 标签不依赖 Markdown 渲染。例如你在包含文件中写了以下内容:

{% highlight javascript %}
console.log('alert');
{% endhighlight %}

highlight 标签是 Liquid 标签(Liquid 会将内容传给 Rouge 来做语法高亮处理)。因此,这段代码确实会被转换成带有语法高亮的 HTML。Jekyll 不需要 Markdown 过滤器来处理 highlight 标签。


Liquid 与 JavaScript 混用无法渲染

假设你尝试将 Liquid 的 assign 标签与 JavaScript 混合使用,比如这样:

<button onclick="someFunction()">Click me</button>

<p id="intro"></p>

<script>
{% assign someContent = "This is some content" %}
function someFunction() {
    document.getElementById("intro").innerHTML = someContent;
}
</script>

这段代码不会生效,因为 assign 标签只在站点构建过程中(Liquid 渲染阶段)可用。在这个 JavaScript 示例中,脚本是在用户点击按钮(“Click me”)后才执行的,那时候 Liquid 的逻辑早已不在了,因此 assign 并不会返回任何值。

不过,你可以使用 Jekyll 的站点变量或 Liquid 将值写入脚本中,在之后执行时使用。比如说,如果你在页面的前置数据中写了这个属性:someContent: "This is some content",你可以这样写:

<button onclick="someFunction()">Click me</button>

<p id="intro"></p>

<script>

function someFunction() {
    document.getElementById("intro").innerHTML = "{{ page.someContent }}";
}
</script>

当 Jekyll 构建站点时,这个 someContent 属性就会被填充进脚本,将 {{ page.someContent }} 替换为 "This is some content"

关键是要记住: Liquid 是在 Jekyll 构建站点时运行的,而不是在浏览器中、用户操作时才执行。

关于在 YAML 中使用 Liquid 的说明

还有一个需要记住的重要细节:Liquid 不会在 YAML 文件或 front matter(前置数据)中渲染
(这和解释顺序无关,但由于这是一个关于元素渲染的常见问题,因此值得一提。)

举个例子,假设你在 _data/mydata.yml 文件中写了一个 highlight 标签:

myvalue: >
  {% highlight javascript %}
  console.log('alert');
  {% endhighlight %}

然后你在页面中尝试这样插入这个值:

{{ site.data.mydata.myvalue }}

结果只会以字符串的形式渲染出来,而不是带语法高亮的代码块。也就是说,Jekyll 不会对 YAML 文件中的 Liquid 进行处理。

要使这段代码真正被渲染为高亮的代码样式,建议使用包含文件(include)来实现