Jekyll 是这个博客网站的主要支撑之一,本文主要介绍其基本语法及使用。

Liquid 中有两种标记类型: 输出 (Output) 和 标签(Tag)。

  • 输出标记 (有些可能解析文本) 被包含在:

{{ 两对配对的花括号之间 }}

  • 标签标记 (不能解析文本) 被包含在:

{% 成对的花括号与百分号之间 %}


输出

一个输出声明被包含在两对配对的花括号之间,当模板被渲染后,其内容会被替换为对应的值。

这是一个简单的例子:


Hello {{name}}
Hello {{user.name}}
Hello {{ 'tobi' }}


表达 (Expressions) 和变量 (Variables)

表达是是有值的声明. Liquid 模板可以在多处使用它们; 常见于在输出声明中, 有时也以参数形式出现在一些标签和过滤器。

Liquid 接受下列形式的表达:

  • 变量. 最基本的的一种表达就是一个变量的名称,Liquid 变量被命名为 Ruby 变量的风格,它们应该含有字母和下划线,应该总是以字母开头, 且不带有任何前缀。也就是说,看起来像 var_name 而不是 $var_name
  • 访问数组、哈希. 如果你有一个表达的值是数组或哈希(通常是一个变量),你可以:
    • my_variable[<键表达>] — 变量的名称,后面紧跟一对方括号,其中包含一个键的表达。
      • 对于数组,键必须是一个整数的字面量或者可以化解为整数的表达。
        • 对于哈希,键必须是一个引号包含的字符串,或者可以化解为字符串的表达。
    • my_hash.key — 哈希也允许使用点 (.) 符号,及变量的名称后紧跟一个句号,随后再紧跟键的名称。这种用法只在键不包含空格的情况有效。与方括号的形式不同的是,这种用法不允许将键名存储在变量中。
    • 注意: 如果一个访问表达的值也是一个数组或哈希,你可以访问通过同样的方式访问这个值,甚至你可以结合两种方法,比如 site.posts[34].title
  • 数组、哈希的大小. 如果你有一个表达的值是数组或哈希,你可以使用在其后紧跟 .size 来获取其元素数量的整数值。
  • 字符串. 字符串字面量必须使用一组成对的双引号 ("") 或单引号 ('') 包含,例如 "my string"'my string'。这两种写法并没有什么不同,也都不允许变量值插入。
  • 整数. 整数必须不带有引号。
  • 布尔(Boolean) 和 nil. 及字面值 truefalsenil

请注意,一个字面的数组 (literal array) 或哈希 (hash) 无法作为表达。数组和哈希必须成为模板,或由标签和输出声明创建间接。


高阶输出: 过滤器 (Filters)

输出标记可以带有过滤器, 用来更改输出结果。你可以通过在输出声明的主表达的后面跟随这些内容来调用:

  • 一个管道符号 (|)

  • 过滤器的名称

  • 以及可选的, 一个冒号 (:) 和用逗号 (,) 分隔的过滤器的额外参数列表。每个额外参数都必须是合法的表达,每个过滤器都预定义了它可以接受的参数及传递顺序。

过滤器也可以通过增加额外过滤器声明(以另一个管道符号开始)串联在一起。前一个过滤器的输出将会作为后一个的输入。


Hello {{ 'tobi' | upcase }}
Hello tobi has {{ 'tobi' | size }} letters!
Hello {{ '*tobi*' | textilize | upcase }}
Hello {{ 'now' | date: "%Y %h" }}

在底层,一个过滤器就是一个有一个或多个参数且有返回值的 Ruby 方法。参数根据位置被传递给过滤器: 第一个参数是管道符号前的表达,额外参数会通过 name: arg1, arg2 语法形式传递。


标准过滤器

  • append - 拼接一个字符串,例如 {{ 'foo' | append:'bar' }} #=> 'foobar'

  • capitalize - 使输入语句首字母大写

  • ceil - 将数组向上增大到最接近的整数 {{ 4.6 | ceil }} #=> 5

  • date - 重新格式化一个日期 (语法参考)

  • default - 返回一个给定的变量值,除非它是空的或者是空字符,例如 {{ undefined_variable | default: "Default value" }} #=> "Default value"

  • divided_by - 整数除法,例如 {{ 10 | divided_by:3 }} #=> 3

  • downcase - 转换输入字符串为小写

  • escape_once - 返回一个转义的 HTML 版本,不影响现有的转义文本

  • escape - HTML 脱义文本

  • first - 获取传入的数组的第一个元素

  • floor - 将数组向下减小到最接近的整数,例如 {{ 4.6 | floor }} #=> 4

  • join - 将确定的字符当做分隔符加入到数组元素之间

  • last - 获取传入的数组的最后一个元素

  • lstrip - 删除字符串开头的所有空白符号

  • map - 从一个给定属性中映射/收集一个数组

  • minus - 减法,例如 {{ 4 | minus:2 }} #=> 2

  • modulo - 模运算,求余,例如 {{ 3 | modulo:2 }} #=> 1

  • newline_to_br - 将所有换行 (\n) 替换为 HTML 换行

  • pluralize - 如果输入不是 1 则返回第二个词,否则返回第一个词,例如 {{ 3 | pluralize: 'item', 'items' }} #=> 'items'

  • plus - 加法,例如 {{ '1' | plus:'1' }} #=> 2, {{ 1 | plus:1 }} #=> 2

  • prepend - 在前面拼接一个字符串 {{ 'bar' | prepend:'foo' }} #=> 'foobar'

  • remove_first - 删除第一个匹配的子字符串,例如 {{ 'barbar' | remove_first:'bar' }} #=> 'bar'

  • remove - 删除所有匹配的子字符串,例如 {{ 'foobarfoobar' | remove:'foo' }} #=> 'barbar'

  • replace_first - 替换第一个匹配的子字符串,例如 {{ 'barbar' | replace_first:'bar','foo' }} #=> 'foobar'

  • replace - 替换所有匹配的子字符串,例如 {{ 'foofoo' | replace:'foo','bar' }} #=> 'barbar'

  • reverse - 反转传入的数组

  • round - 将输入舍入到最接近的整数,并置为十进制,例如 {{ 4.5612 | round: 2 }} #=> 4.56

  • rstrip - 删除字符串结尾的所有空白符号

  • size - 返回数组或字符串的大小

  • slice - 通过偏移和长度 (offset and length) 分隔字符串,例如 {{ "hello" | slice: -3, 3 }} #=> llo

  • sort - 对数组元素进行排序

  • split - 将一串字符串根据匹配模式分割成数组,例如 {{ "a~b" | split:"~" }} #=> ['a','b']

  • strip_html - 删除字符串中的 HTML 元素

  • strip_newlines - 删除字符串中有所换行 (\n)

  • strip - 删除字符串两端所有空白符号

  • times - 乘法 e.g {{ 5 | times:4 }} #=> 20

  • truncate - 将字符串截断为若干字符,如果有第二个参数将拼接到字符串,例如 {{ 'foobarfoobar' | truncate: 5, '.' }} #=> 'foob.'

  • truncatewords - 将字符串截断为若干词

  • uniq - 删除数组中重复的元素, 可选的,使用给定的属性来测试唯一性

  • upcase - 转换输入字符串为大写

  • url_encode - URL 编码字符串


标签

标签被用在模板逻辑中,新的标签很容易开发。

这是一个目前支持的标签的列表:

  • assign - 将一些值赋给变量

  • capture - 块标签,把一些文本捕捉到变量中

  • case - 块标签,标准的 case 语句

  • comment - 块标签,将一块文本作为注释

  • cycle - 通常用于循环轮换值,比如颜色或 DOM

  • for - 用于循环

  • break - 结束 for 循环

  • continue 进入当前循环的下一次循环过程

  • if - 标准 if/else

  • include - 包含其他模块,对于区块化非常有效

  • raw - 暂时性的禁用标签解析,脱义

  • unless - if 的简版

注释

任何被你放在 {% comment %}{% endcomment %} 标签之间的内容都会被解析为注释。


今年我们交易 {% comment %} 损失 {% endcomment %} 了一百万美元

Raw

Raw 暂时性的禁用标签的解析。这在展示一些可能产生冲突的语法内容时很有用,例如我们在这篇使用 Jekyll 的博文中展示 Jekyll 代码,这代码是被包含在 Raw 标签之间的,所以不被解析。

{% raw %}
  In Handlebars, {{ this }} will be HTML-escaped, but {{{ that }}} will not.
{% endraw %}

If / Else

if / else 声明在其它语言中应该已经被大家熟知了,Liquid 实现中它是这样使用的:

  • {% if <条件> %} ... {% endif %} — 包含一组只在条件成立时才会执行的内容。

  • {% elsif <条件> %} — 可以被可选的用于 if ... endif 块中。它指明另一个条件,当第一个 if 的条件不成立时,Liquid 将会尝试 elsif,如果其条件成立将会执行其包含的一组内容,你可以在 if 块中使用任意数量的 elsifs

  • {% else %} — 可以被可选的用于 if ... endif 块,放在任意一个 elsif 标签后,如果它前面所有的条件都不成立,Liquid 将会执行 else 标签后包含的一组内容。

  • {% unless <条件> %} ... {% endunless %}if 声明的相反版本,不要在 unless 声明后使用 elsifelse

ifelsifunless 标签的条件应该一个普通的 Liquid 表达,或 Liquid 表达的比较。 请注意,比较操作符被实现为类似 if 的标签,他们在 Liquid 的其它任何地方都不起作用。

可用的比较操作符有:

  • ==!=,<> — 等于和不等于
    • 还有一个 empty 可以用来比较数组,如果数组没有元素则成立
  • <<=>>= — 小于、小于等于、大于、大于等于
  • contains — 对 Rubyinclude? 方法的包装,可用于字符串、数组和哈希。如果其左侧参数是字符串而右侧不是, 右侧将被字符串化。

可用的布尔 (Boolean) 操作符有:

  • and
  • or

请注意,没有 not 操作符。

另外还要注意,你 不能 使用圆括号 (())来控制操作顺序。操作符的优先级是不确定的。所以如果有疑问时,使用嵌套的 if 声明而不使用这些操作符冒险。

Liquid 表达是在 Ruby 式看似成立的情况下测试的:

  • true 是真
  • false 是假
  • 任何字符串都是真,包括空字符串
  • 任何数组都是真
  • 任何哈希都是真
  • 任何不存在的、nil 值 (比如丢失元素的哈希) 都是假.

{% if user %}
  你好 {{ user.name }}
{% endif %}


# 和上面相同
{% if user != null %}
  你好 {{ user.name }}
{% endif %}


{% if user.name == 'tobi' %}
  你好 tobi
{% elsif user.name == 'bob' %}
  你好 bob
{% endif %}


{% if user.name == 'tobi' or user.name == 'bob' %}
  你好 tobi 或 bob
{% endif %}


{% if user.name == 'bob' and user.age > 45 %}
  你好老 bob
{% endif %}


{% if user.name != 'tobi' %}
  你好不是tobi的家伙
{% endif %}


# 和上面相同
{% unless user.name == 'tobi' %}
  你好不是tobi的家伙
{% endunless %}


# 检查数组大小
{% if user.payments == empty %}
   你没有付款 !
{% endif %}

{% if user.payments.size > 0  %}
   你已经付款 !
{% endif %}


{% if user.age > 18 %}
   点这里登录
{% else %}
   抱歉你的年龄不够
{% endif %}


# array = 1,2,3
{% if array contains 2 %}
   数组包含2
{% endif %}


# string = '你好世界'
{% if string contains 'hello' %}
   字符串包含 '你好'
{% endif %}

Case 表达

如果你需要更多的条件,你可以使用 case 声明:


{% case 条件 %}
{% when 1 %}
是 1
{% when 2 or 3 %}
是 2 或 3
{% else %}
... else ...
{% endcase %}

示例:


{% case template %}

{% when 'label' %}
     // {{ label.title }}
{% when 'product' %}
     // {{ product.vendor | link_to_vendor }} / {{ product.title }}
{% else %}
     // {{page_title}}
{% endcase %}

Cycle

可能你经常需要在不同的颜色或者类似任务中轮流切换,Liquid 对这样的操作拥有内建支持,使用 cycle 标签即可。


{% cycle 'one', 'two', 'three' %}
{% cycle 'one', 'two', 'three' %}
{% cycle 'one', 'two', 'three' %}
{% cycle 'one', 'two', 'three' %}

产生的结果是


one
two
three
one

如果一组 cycle 没有命名,那默认情况下有用相同参数的会被认为是一个组。

如果你希望完全控制 cycle 组,你可以指定一个组名,这个组名甚至可以是一个变量。


{% cycle 'group 1': 'one', 'two', 'three' %}
{% cycle 'group 1': 'one', 'two', 'three' %}
{% cycle 'group 2': 'one', 'two', 'three' %}
{% cycle 'group 2': 'one', 'two', 'three' %}

产生的结果是


one
two
one
two

For 循环

Liquid 允许 for 循环遍历集合:


{% for item in array %}
  {{ item }}
{% endfor %}

支持的集合类型

for 可以用于数组、哈希以及指定范围内的数值。

用于哈希时, item[0] 包含了键,而 item[1] 则包含值:


{% for item in hash %}
  {{ item[0] }}: {{ item[1] }}
{% endfor %}

除了用于已存在的集合循环,你也可以对一定范围内数值使用循环,它们看起来 像(1..10) 这样 — 圆括号包含着一个起始值、两个英文句号和一个结束值。起始值和结束值都必须是整数或者可以被化解为整数的表达。


# if item.quantity is 4...
{% for i in (1..item.quantity) %}
  {{ i }}
{% endfor %}
# results in 1,2,3,4

循环控制

你可以通过下列标签控制一个循环:

  • {% continue %} — 立即终止当前轮次的循环,进入 for 循环下一轮次

  • {% break %} — 立即终止整个循环,忽略未执行的轮次

二者均在搭配 if 声明使用时才有用处。


{% for page in pages %}
# 跳过任何存在于 hidden_pages 数组中的内容,但不影响其他内容的循环
{% if hidden_pages contains page.url %}
    {% continue %}
{% endif %}
# 如果不在该数组中
* [page.title](page.url)
{% endfor %}

{% for page in pages %}
* [page.title](page.url)
# 完成 cutoff_page 后立即跳出循环,继续去执行循环后面的内容
{% if cutoff_page == page.url %}
    {% break %}
{% endif %}
{% endfor %}

辅助变量

在每一个 for 循环中,这些辅助变量都可以使用:


forloop.length      # => 整个循环的总次数
forloop.index       # => 当前轮次的索引编号
forloop.index0      # => 当前轮次的索引编号,最小值从0开始
forloop.rindex      # => 剩余循环次数
forloop.rindex0     # => 剩余循环次数,最小值从0开始
forloop.first       # => 本轮是否为首轮循环
forloop.last        # => 本轮是否为末轮循环

可选参数

这是一系列 for 标签的可选参数,它们可以影响你在循环中获得的元素内容和顺序:

  • limit:<整数> 允许你限制最大循环次数

  • offset:<整数> 允许你指定从集合的什么位置开始循环

  • reversed 倒序循环集合

循环元素:


# array = [1,2,3,4,5,6]
{% for item in array limit:2 offset:2 %}
  {{ item }}
{% endfor %}
# results in 3,4

倒序循环:


{% for item in collection reversed %} {{item}} {% endfor %}

一个 for 循环可以使用一个可选的 else 分句,在集合没有元素时显示一些文字:


    # items => []
    {% for item in items %}
       {{ item.title }}
    {% else %}
       没有任何元素!
    {% endfor %}

变量赋值

你可以存储数据到变量中,从而在输出或其他标签中使用。创建变量最简单的方式是使用 assign 标签,它的语法很直截了当:


{% assign name = 'freestyle' %}

{% for t in collections.tags %}{% if t == name %}
  <p>Freestyle!</p>
{% endif %}{% endfor %}

另一种方式是将 true / false 复制给变量:


{% assign freestyle = false %}

{% for t in collections.tags %}{% if t == 'freestyle' %}
  {% assign freestyle = true %}
{% endif %}{% endfor %}

{% if freestyle %}
  <p>Freestyle!</p>
{% endif %}

如果你想将一些字符串组合为一个整体单一的字符串并保存到变量,你可以使用 capture 标签,这个标签会将任何交于它的内容捕获,然后将捕获的内容交给变量而不是输出到屏幕。


  {% capture attribute_name %}{{ item.title | handleize }}-{{ i }}-color{% endcapture %}

  <label for="{{ attribute_name }}">Color:</label>
  <select name="attributes[{{ attribute_name }}]" id="{{ attribute_name }}">
    <option value="red">Red</option>
    <option value="green">Green</option>
    <option value="blue">Blue</option>
  </select>

好了,基础的内容大概就是这些了。