Zola教程:2-shortcodes

2023-05-31

rust zola

概念

Zola借用了WordPress的短代码(shortcodes)概念,在Zola中,短代码指的是md文件中可以用的模板片段,通常位于templates/shortcodes目录。

shortcodes通常用于两个场景:

  • 插入复杂的HTML,markdown格式比较适用于创作,但是不方便内嵌HTML或者样式
  • 易于完成重复的基于数据的任务

创建shortcode

来看一个例子,我们在templates/shortcodes目录下创建一个文件youtube.html,内容如下:

<div {% if class %}class="{{class}}"{% endif %}>
 <iframe
 src="https://www.youtube.com/embed/{{id}}{% if autoplay %}?autoplay=1{% endif %}"
 webkitallowfullscreen
 mozallowfullscreen
 allowfullscreen>
 </iframe>
</div>

这段模板代码可以渲染出内嵌youtube视频的HTML片段,包含了一个必传的参数id,以及可传的参数class。这就是一个zola的shortcode,文件名youtube就是shortcode的名字,在md文件中可以用这个名字来引用这段shortcode。由于zola会将inline的HTML节点(例如<a>,<span>)宣传成<p>,如果不希望这样,可以把HTML片段用<div>包起来。

再来看一个md文件实现shortcode的例子,在templates/shortcodes目录下创建文件book.md:

{% set data = load\_data(path=path) -%}
{% for book in data.books %}
### {{ book.title }}

{{ book.description | safe }}
{% endfor %}

这个叫books的shortcode支持一个path参数,说明.toml文件名,load_data函数可以读取这个.toml文件的内容获取book列表并渲染。

shortcode是在markdown解析之前渲染的,所以不知道页面内容的结构。如果想实现相应的功能,可以使用get_page/get_section/get_taxonomy/get_taxonomy_term 这些全局函数,但是要注意的是这些函数只有在zola serve的时候才可用,zola build的时候是不能用的。

使用shortcode

有两类shortcode:

  • 一类没有body,例如前面youtube的例子
  • 一类有body,例如一个引用样式的shortcode,参考后面的例子

不管哪种类型,参数都必须有名字,并且参数必须全部传入模板,即使没有参数,shortcode名字后面也必须跟括号。shortcode的名字和参数的名字都必须遵循一定的规则,只能是数字、字母、下划线的组合。不过,参数名虽然可以是数字,但是通常没法在模板中使用数字作为参数名。

参数的值可以是以下类型,如果参数值格式错误,将被忽略。:

  • string:用双引号、单引号或反撇号括起来
  • bool:true 或 false
  • float:浮点数
  • integer:整数
  • array:包含除了数组的任意类型的数组

在shortcode中可以使用变量page或者section,取决于其使用场景。另外还可以使用config变量,这些名字将覆盖传入的参数,所以不要用这些名字作为参数名。

没有body的shortcode

对于这类shortcode,只需要简单调用,就好像Tera函数(模板内置函数)那样,下面是使用前面的youtube shortcode的例子:

Here is a YouTube video:

{ { youtube(id="dQw4w9WgXcQ") } }

{ { youtube(id="dQw4w9WgXcQ", autoplay=true) } }

An inline { { youtube(id="dQw4w9WgXcQ", autoplay=true, class="youtube") } } shortcode

要注意的是,如果在文件里需要添加一些类似shortcode的文本,又不希望zola去渲染它们(例如展示示例代码),可以用 {{/* 和 */}} 代替 {{ 和 }}。

有body的shortcode

假定我们有个名为quote.html的shortcode模板,其内容为:

<blockquote>
 {{ body }} <br>
 -- {{ author}}
</blockquote>

在md文件中我们可以这样使用这个shortcode:

As someone said:

{ % quote(author="Vincent") % }
A quote
{ % end % }

shortcode的body部分会自动作为body参数传入模板。

没有参数的shortcode

不管哪种类型的shortcode,括号都是必须的。如果shortcode后面不跟括号,将会被渲染成普通文本,不会有任何警告。下面是一个例子,我们可以看到,aside.html这个shortcode没有任何参数:

<aside>
 {{ body }}
</aside>

在md文件中,这个shortcode需要这样引用:

Readers can refer to the aside for more information.

{ % aside() % }
An aside
{ % end % }

shortcode上下文

除了传入的参数,shortcode还可以使用一些变量:

nth

shortcode在当前md文件中的执行次数。来看下面的例子,对于shortcode模板true_statement.html:

<p id="number{{ nth }}">{{ value }} is equal to {{ nth }}.</p>

在md文件中这样使用这个shortcode:

{{ true\_statement(value=1) }}
{{ true\_statement(value=2) }}

可以试一下显示效果。在实现类似于便签的时候,这个变量可以帮你实现自定义的标号。

lang

当前的语言。这个变量对多语言支持非常有用,例如下面的例子会按照语言展示不同的图书封面:

![Book cover in {{ lang }}](cover.{{ lang }}.png)

page 或section

与普通模板相比,shortcode可以使用的变量略少一些。下面的的变量使用的时候是空值,这是因为在处理section之前markdown的渲染已经结束:

  • translations
  • backlinks
  • pages

page有一个非常有用的属性是colocated_path,可以让你传资源名的时候不需要重复传完整的路径。例如在load_data或resize_image 中可以用到这个属性:

{% set resized = resize\_image(format="jpg", path=page.colocated\_path ~ img\_name, width=width, op="fit\_width") %}
<img alt="{{ alt }}" src="{{ resized.url | safe }}" />

示例

YouTube

参数

  • id: the video id (mandatory)
  • playlist: the playlist id (optional)
  • class: a class to add to the
    surrounding the iframe
  • autoplay: when set to "true", the video autoplays on load

代码

<div {% if class %}class="{{class}}"{% endif %}>
 <iframe src="https://www.youtube-nocookie.com/embed/{{id}}{% if playlist %}?list={{playlist}}{% endif %}{% if autoplay %}?autoplay=1{% endif %}" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
</div>

用法

{ { youtube(id="dCKeXuVHl1o") } }

{ { youtube(id="dCKeXuVHl1o", playlist="RDdQw4w9WgXcQ") } }

{ { youtube(id="dCKeXuVHl1o", autoplay=true) } }

{ { youtube(id="dCKeXuVHl1o", autoplay=true, class="youtube") } }
<div>
{% for asset in page.assets -%}
 {%- if asset is matching("[.](jpg|png)$") -%}
 {% set image = resize\_image(path=asset, width=240, height=180) %}
 <a href="{{ get\_url(path=asset) }}" target="\_blank">
 <img src="{{ image.url }}" />
 </a>
 {%- endif %}
{%- endfor %}
</div>