概念
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") } }
Image Gallery
<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>