创建你自己的短代码
短代码是一种将模板整合到小的、可重用的片段中的方法,你可以将这些片段直接嵌入到你的内容中。
创建自定义短代码
Hugo 的内置短代码涵盖了许多常见但并非全部的用例。幸运的是,Hugo 提供了轻松创建自定义短代码来满足你的网站需求的功能。
文件位置
要创建短代码,请将 HTML 模板放在 layouts/shortcodes
目录中。仔细考虑文件名,因为短代码名称将镜像该文件名,但不包含 .html
扩展名。例如,layouts/shortcodes/myshortcode.html
将使用 {{< myshortcode />}}
或 {{% myshortcode /%}}
调用。
你可以将短代码组织在子目录中,例如在 layouts/shortcodes/boxes
中。然后,这些短代码将可以通过其相对路径访问,例如
{{< boxes/square >}}
注意正斜杠。
模板查找顺序
Hugo 根据短代码名称、当前输出格式和当前语言选择短代码模板。下面的示例按特异性降序排序。最不具体的路径在列表底部。
短代码名称 | 输出格式 | 语言 | 模板路径 |
---|---|---|---|
foo | html | zh | layouts/shortcodes/foo.zh.html |
foo | html | zh | layouts/shortcodes/foo.html.html |
foo | html | zh | layouts/shortcodes/foo.html |
foo | html | zh | layouts/shortcodes/foo.html.zh.html |
短代码名称 | 输出格式 | 语言 | 模板路径 |
---|---|---|---|
foo | rss | zh | layouts/shortcodes/foo.zh.xml |
foo | rss | zh | layouts/shortcodes/foo.rss.xml |
foo | rss | zh | layouts/shortcodes/foo.zh.html |
foo | rss | zh | layouts/shortcodes/foo.rss.zh.xml |
foo | rss | zh | layouts/shortcodes/foo.xml |
foo | rss | zh | layouts/shortcodes/foo.html.zh.html |
foo | rss | zh | layouts/shortcodes/foo.html.html |
foo | rss | zh | layouts/shortcodes/foo.html |
请注意,主题或模块提供的模板始终优先。
位置参数与命名参数
你可以使用以下类型的参数创建短代码
- 位置参数
- 命名参数
- 位置参数或命名参数
在具有位置参数的短代码中,参数的顺序很重要。如果短代码具有单个必需值,则位置参数需要内容作者较少的输入。
对于具有多个或可选参数的更复杂的布局,命名参数效果最佳。虽然不如简洁,但命名参数需要内容作者较少的记忆,并且可以按任何顺序添加到短代码声明中。
允许两种类型的参数对于复杂的布局很有用,在这种布局中,你希望设置可以被用户轻松覆盖的默认值。
访问参数
所有短代码参数都可以通过 .Get
方法访问。你是向 .Get
方法传递字符串还是数字取决于你是访问命名参数还是位置参数。
要按名称访问参数,请使用 .Get
方法,后跟以引号括起来的命名参数
{{ .Get "class" }}
要按位置访问参数,请使用 .Get
后跟数字位置,请记住位置参数从零开始索引
{{ .Get 0 }}
对于第二个位置,你只需使用
{{ .Get 1 }}
当输出取决于是否设置了参数时,with
非常有用
{{ with .Get "class" }} class="{{ . }}"{{ end }}
.Get
也可以用来检查是否已提供参数。当条件取决于其中一个或两个值时,这最有用
{{ if or (.Get "title") (.Get "alt") }} alt="{{ with .Get "alt" }}{{ . }}{{ else }}{{ .Get "title" }}{{ end }}"{{ end }}
.Inner
.Inner
方法返回开始和结束短代码标记之间的内容。要检查 .Inner
是否返回除空格之外的任何内容
{{ if strings.ContainsNonSpace .Inner }}
Inner is not empty
{{ end }}
.Params
短代码中的 .Params
方法返回传递给短代码的参数,以用于更复杂的用例。你还可以使用以下逻辑访问更高范围的参数
- $.Params
- 这些是直接传递到短代码声明中的参数(例如,YouTube 视频 ID)
- $.Page.Params
- 指页面的参数;这里的“页面”指的是声明短代码的内容文件(例如,可以通过
$.Page.Params.shortcode_color
访问内容前言中的shortcode_color
字段)。 - $.Site.Params
- 指在站点配置中定义的参数。
.IsNamedParams
.IsNamedParams
方法检查短代码声明是否使用了命名参数,并返回一个布尔值。
例如,您可以创建一个 image
短代码,它可以接受一个 src
命名参数或第一个位置参数,具体取决于内容作者的偏好。假设 image
短代码的调用方式如下
{{< image src="images/my-image.jpg" >}}
然后,您可以将以下内容作为短代码模板的一部分包含进来
{{ if .IsNamedParams }}
<img src="{{ .Get "src" }}" alt="">
{{ else }}
<img src="{{ .Get 0 }}" alt="">
{{ end }}
有关 .IsNamedParams
的实际应用,请参阅下面的 Vimeo 短代码示例。
短代码也可以嵌套。在嵌套的短代码中,您可以使用 .Parent
短代码方法访问父短代码上下文。这对于从根继承非常有用。
检查是否存在
您可以通过在页面模板中调用 .HasShortcode
并提供短代码的名称来检查特定短代码是否在页面上使用。当您想在头部包含仅由该短代码使用的特定脚本或样式时,这非常有用。
自定义短代码示例
以下是通过 /layouts/shortcodes
中的短代码模板文件创建的不同类型短代码的示例。
单字示例:year
假设您希望在内容文件中保持版权年份的最新状态,而无需不断检查 Markdown。您的目标是能够按如下方式调用短代码
{{< year >}}
{{ now.Format "2006" }}
单位置示例:youtube
嵌入视频是 Markdown 内容的常见添加项。以下是 Hugo 内置 YouTube 短代码使用的代码
{{< youtube 09jf3ow9jfw >}}
将加载 /layouts/shortcodes/youtube.html
的模板
<div class="embed video-player">
<iframe class="youtube-player" type="text/html" width="640" height="385" src="https://www.youtube.com/embed/{{ index .Params 0 }}" allowfullscreen frameborder="0">
</iframe>
</div>
<div class="embed video-player">
<iframe class="youtube-player" type="text/html"
width="640" height="385"
src="https://www.youtube.com/embed/09jf3ow9jfw"
allowfullscreen frameborder="0">
</iframe>
</div>
单命名示例:image
假设您想创建自己的 img
短代码,而不是使用 Hugo 内置的 figure
短代码。您的目标是能够在内容文件中按如下方式调用短代码
{{< img src="/media/spf13.jpg" title="Steve Francia" >}}
您已经在 /layouts/shortcodes/img.html
中创建了短代码,它将加载以下短代码模板
<!-- image -->
<figure {{ with .Get "class" }}class="{{ . }}"{{ end }}>
{{ with .Get "link" }}<a href="{{ . }}">{{ end }}
<img src="{{ .Get "src" }}" {{ if or (.Get "alt") (.Get "caption") }}alt="{{ with .Get "alt" }}{{ . }}{{ else }}{{ .Get "caption" }}{{ end }}"{{ end }} />
{{ if .Get "link" }}</a>{{ end }}
{{ if or (or (.Get "title") (.Get "caption")) (.Get "attr") }}
<figcaption>{{ if isset .Params "title" }}
<h4>{{ .Get "title" }}</h4>{{ end }}
{{ if or (.Get "caption") (.Get "attr") }}<p>
{{ .Get "caption" }}
{{ with .Get "attrlink" }}<a href="{{ . }}"> {{ end }}
{{ .Get "attr" }}
{{ if .Get "attrlink" }}</a> {{ end }}
</p> {{ end }}
</figcaption>
{{ end }}
</figure>
<!-- image -->
将呈现为
<figure>
<img src="/media/spf13.jpg" />
<figcaption>
<h4>Steve Francia</h4>
</figcaption>
</figure>
单灵活示例:vimeo
{{< vimeo 49718712 >}}
{{< vimeo id="49718712" class="flex-video" >}}
将加载位于 /layouts/shortcodes/vimeo.html
的模板
{{ if .IsNamedParams }}
<div class="{{ if .Get "class" }}{{ .Get "class" }}{{ else }}vimeo-container{{ end }}">
<iframe src="https://player.vimeo.com/video/{{ .Get "id" }}" allowfullscreen></iframe>
</div>
{{ else }}
<div class="{{ if len .Params | eq 2 }}{{ .Get 1 }}{{ else }}vimeo-container{{ end }}">
<iframe src="https://player.vimeo.com/video/{{ .Get 0 }}" allowfullscreen></iframe>
</div>
{{ end }}
将呈现为
<div class="vimeo-container">
<iframe src="https://player.vimeo.com/video/49718712" allowfullscreen></iframe>
</div>
<div class="flex-video">
<iframe src="https://player.vimeo.com/video/49718712" allowfullscreen></iframe>
</div>
成对示例:highlight
以下内容取自 highlight
,它是 Hugo 自带的内置短代码。
{{< highlight html >}}
<html>
<body> This HTML </body>
</html>
{{< /highlight >}}
highlight
短代码的模板使用以下代码,该代码已包含在 Hugo 中
{{ .Get 0 | highlight .Inner }}
HTML 示例代码块的呈现输出将如下所示
<div class="highlight" style="background: #272822"><pre style="line-height: 125%"><span style="color: #f92672"><html></span>
<span style="color: #f92672"><body></span> This HTML <span style="color: #f92672"></body></span>
<span style="color: #f92672"></html></span>
</pre></div>
嵌套短代码:图片库
当所讨论的短代码在父短代码的上下文中调用时,Hugo 的 .Parent
短代码方法提供对父短代码上下文的访问权限。这提供了一个继承模型。
以下示例是人为设计的,但演示了该概念。假设您有一个 gallery
短代码,它需要一个名为 class
的命名参数
<div class="{{ .Get "class" }}">
{{ .Inner }}
</div>
您还有一个 img
短代码,它具有一个名为 src
的命名参数,您想在 gallery
和其他短代码内部调用它,以便父短代码定义每个 img
的上下文
{{- $src := .Get "src" -}}
{{- with .Parent -}}
<img src="{{ $src }}" class="{{ .Get "class" }}-image">
{{- else -}}
<img src="{{ $src }}">
{{- end -}}
然后,您可以在内容中按如下方式调用短代码
{{< gallery class="content-gallery" >}}
{{< img src="/images/one.jpg" >}}
{{< img src="/images/two.jpg" >}}
{{< /gallery >}}
{{< img src="/images/three.jpg" >}}
这将输出以下 HTML。请注意,前两个 img
短代码如何继承通过调用父 gallery
设置的 content-gallery
的 class
值,而第三个 img
仅使用 src
<div class="content-gallery">
<img src="/images/one.jpg" class="content-gallery-image">
<img src="/images/two.jpg" class="content-gallery-image">
</div>
<img src="/images/three.jpg">
短代码中的错误处理
使用 errorf
模板函数和 Name
以及 Position
短代码方法来生成有用的错误消息
{{ with .Get "name" }}
<p>Hello, my name is {{ . }}.</p>
{{ else }}
{{ errorf "The %q shortcode requires a 'name' argument. See %s" .Name .Position }}
{{ end }}
当上述操作失败时,您将看到类似如下的 ERROR
消息
ERROR The "greeting" shortcode requires a 'name' argument. See "/home/user/project/content/_index.md:12:1"
内联短代码
您还可以在内联实现您的短代码 - 例如,在您在内容文件中使用它们的地方。这对于您只需要在一个地方使用的脚本可能很有用。
此功能默认处于禁用状态,但可以在您的站点配置中启用
security:
enableInlineShortcodes: true
[security]
enableInlineShortcodes = true
{
"security": {
"enableInlineShortcodes": true
}
}
出于安全原因,默认情况下禁用它。Hugo 的模板处理使用的安全模型假定模板作者是受信任的,但内容文件不是,因此模板可以安全地防止畸形输入数据注入。但在大多数情况下,您也可以完全控制内容,然后 enableInlineShortcodes = true
被认为是安全的。但需要注意一点:它允许从内容文件中执行临时的 Go 文本模板。
一旦启用,您就可以在内容文件中执行此操作
{{< time.inline >}}{{ now }}{{< /time.inline >}}
以上操作将打印当前的日期和时间。
请注意,内联短代码的内部内容会作为 Go 文本模板进行解析和执行,其上下文与常规短代码模板相同。
这意味着可以通过 .Page.Title
等访问当前页面。这也意味着不存在“嵌套的内联短代码”的概念。
如果需要,可以使用自闭合语法在同一内容文件中稍后重用相同的内联短代码,并使用不同的参数
{{< time.inline />}}