HUGO
Menu
GitHub 87548 stars Mastodon

模板入门介绍

Hugo 模板语法的入门介绍。
We did a complete overhaul of Hugo’s template system in v0.146.0. We’re working on getting all of the relevant documentation up to date, but until then, see this page.

template(模板)是包?template actions(模板动作)的文件,位于项目、主题或模块?layouts 目录内

模板使用 变量函数方法 将您的内容、资源和数据转换为已发布的页面。

Hugo 使用 Go 的 text/templatehtml/template 包。

text/template 包实现数据驱动的模板以生成文本输出,而 html/template 包实现数据驱动的模板以生成 HTML 输出,防止代码注入。

默认情况下,Hugo 在渲染 HTML 文件时使用 html/template 包。

例如,此 HTML 模板初始化 $v1$v2 变量,然后在 HTML 段落中显示它们及其乘积。

{{ $v1 := 6 }}
{{ $v2 := 7 }}
<p>{{ $v1 }}{{ $v2 }} 的乘积是 {{ mul $v1 $v2 }}</p>

虽然 HTML 模板最常见,但您可以为任何 输出格式 创建模板,包括 CSV、JSON、RSS 和纯文本。

上下文

在创建模板之前,需要理解的最重要概念是 上下文,即传递给每个模板的数据。数据可以是一个简单的值,或者更常见的是 对象 及其关联的 方法

例如,页面 模板接收一个 Page 对象,Page 对象提供方法来返回值或执行操作。

当前上下文

在模板中,点 (.) 表示当前上下文。

layouts/page.html
<h2>{{ .Title }}</h2>

在上面的示例中,点表示 Page 对象,我们调用其 Title 方法来返回 前言 中定义的标题。

当前上下文可能在模板中发生变化。例如,在模板的顶部,上下文可能是 Page 对象,但我们在 rangewith 块中将上下文重新绑定为其他值或对象。

layouts/page.html
<h2>{{ .Title }}</h2>

{{ range slice "foo" "bar" }}
  <p>{{ . }}</p>
{{ end }}

{{ with "baz" }}
  <p>{{ . }}</p>
{{ end }}

在上面的示例中,当我们 遍历 切片 值时,上下文会发生变化。在第一次迭代中,上下文是 “foo”,在第二次迭代中,上下文是 “bar”。在 with 块内,上下文是 “baz”。Hugo 将上述内容渲染为:

<h2>我的页面标题</h2>
<p>foo</p>
<p>bar</p>
<p>baz</p>

模板上下文

rangewith 块中,您可以通过在点前添加美元符号 ($) 来访问传递给模板的上下文:

layouts/page.html
{{ with "foo" }}
  <p>{{ $.Title }} - {{ . }}</p>
{{ end }}

Hugo 将此渲染为:

<p>我的页面标题 - foo</p>

在继续阅读之前,请确保您彻底理解 上下文 的概念。新手用户最常见的模板错误与上下文有关。

动作

在上面的示例中,成对的开始和结束大括号表示模板动作的开始和结束,模板动作是模板内的数据评估或控制结构。

模板动作可以包含字面值(布尔值字符串整数浮点数)、当前上下文变量函数方法nil 关键字。

layouts/page.html
{{ $convertToLower := true }}
{{ if $convertToLower }}
  <h2>{{ strings.ToLower .Title }}</h2>
{{ end }}

在上面的示例中:

  • $convertToLower 是一个变量
  • true 是一个字面布尔值
  • if 是控制结构的开始
  • strings.ToLower 是一个将所有字符转换为小写的函数
  • TitlePage 对象上的一个方法
  • end 是控制结构的结束

Hugo 将上述内容渲染为:

  
  
    <h2>我的页面标题</h2>
  

空白

注意到上一个示例中的空行和缩进了吗?虽然在生产环境中通常会对输出进行压缩,因此这些空白无关紧要,但您可以使用带连字符的模板动作分隔符来删除相邻的空白:

layouts/page.html
{{- $convertToLower := true -}}
{{- if $convertToLower -}}
  <h2>{{ strings.ToLower .Title }}</h2>
{{- end -}}

Hugo 将此渲染为:

<h2>我的页面标题</h2>

空白包括空格、水平制表符、回车符和换行符。

引号字符

Hugo 模板使用不同的引号字符来定义文本和字符的处理方式。

使用双引号表示 解释型字符串字面值。这些将反斜杠解释为特殊指令:

{{ print "Hello world\u0021" }} → Hello world!

使用反引号表示 原始字符串字面值。这些忽略反斜杠并按字面处理每个字符:

{{ print `Hello world\u0021` }} → Hello world\u0021

使用单引号表示 rune 字面值。与字符串不同,这些将单个字符表示为其数字 Unicode 值:

{{ print '!' }} → 33

实际上,您几乎不会在模板代码中使用 rune 字面值。它们最常用于低级编程;在 Hugo 模板中,您几乎总是需要字符串。

管道

在模板动作中,您可以将值 管道 传递给函数或方法。管道值成为函数或方法的最后一个参数。例如,这些是等价的:

{{ strings.ToLower "Hugo" }} → hugo
{{ "Hugo" | strings.ToLower }} → hugo

您可以将一个函数或方法的结果管道传递给另一个。例如,这些是等价的:

{{ strings.TrimSuffix "o" (strings.ToLower "Hugo") }} → hug
{{ "Hugo" | strings.ToLower | strings.TrimSuffix "o" }} → hug

这些也是等价的:

{{ mul 6 (add 2 5) }} → 42
{{ 5 | add 2 | mul 6 }} → 42

请记住,管道值成为您管道传递到的函数或方法的最后一个参数。

行分割

您可以将模板动作分割到两行或多行。例如,这些是等价的:

{{ $v := or $arg1 $arg2 }}

{{ $v := or 
  $arg1
  $arg2
}}

您还可以将 原始字符串字面值 分割到两行或多行。例如,这些是等价的:

{{ $msg := "This is line one.\nThis is line two." }}

{{ $msg := `This is line one.
This is line two.`
}}

Nil

除了在比较中使用 nil 关键字外,您不能将其用作任何函数或方法的参数,也不能将其分配给变量。例如,这些是 nil 关键字的有效用法:

{{ if gt 42 nil }}
  <p>42 大于 nil</p>
{{ end }}

{{ $pages := where .Site.RegularPages "Params.color" "ne" nil }}

另一方面,这些是无效的:

{{ $a := nil }} 
{{ add 3 nil }} 
{{ nil | print}}

上述动作会抛出错误。

变量

变量是用户定义的 标识符,前面加上美元符号 ($),表示在模板动作中初始化或分配的任何数据类型的值。例如,$foo$bar 是变量。

变量可以包含 标量切片映射对象

使用 := 初始化变量,使用 = 将值分配给之前初始化的变量。例如:

{{ $total := 3 }}
{{ range slice 7 11 21 }}
  {{ $total = add $total . }}
{{ end }}
{{ $total }} → 42

ifrangewith 块内初始化的变量的作用域仅限于该块。在这些块外初始化的变量的作用域为模板。

对于表示切片或映射的变量,使用 index 函数返回所需的值。

{{ $slice := slice "foo" "bar" "baz" }}
{{ index $slice 2 }} → baz

{{ $map := dict "a" "foo" "b" "bar" "c" "baz" }}
{{ index $map "c" }} → baz

切片和数组从零开始;元素 0 是第一个元素。

对于表示映射或对象的变量,链接 标识符以返回所需的值或访问所需的方法。

{{ $map := dict "a" "foo" "b" "bar" "c" "baz" }}
{{ $map.c }} → baz

{{ $homePage := .Site.Home }}
{{ $homePage.Title }} → 我的主页标题

如上所示,对象和方法名称是大写的。虽然不要求,但为了避免混淆,我们建议变量和映射键名以小写字母或下划线开头。

函数

函数在模板动作中使用,接受一个或多个参数并返回一个值。与方法不同,函数不与对象关联。

Go 的 text/template 和 html/template 包提供了一小组用于通用用途的函数、运算符和语句。有关详细信息,请参阅函数文档的 go-templates 部分。

Hugo 提供数百个按命名空间分类的自定义 函数。例如,strings 命名空间包括这些和其他函数:

函数 别名
strings.ToLower lower
strings.ToUpper upper
strings.Replace replace

如上所示,常用函数有别名。在模板中使用别名以减少代码长度。

调用函数时,用空格将参数与函数以及参数彼此分开。例如:

{{ $total := add 1 2 3 4 }}

方法

方法在模板动作中使用并与对象关联,接受零个或多个参数,要么返回值,要么执行操作。

最常访问的对象是 PageSite 对象。这是每个对象可用 方法 的一小部分示例。

对象 方法 描述
Page Date 返回给定页面的日期。
Page Params 返回给定页面前言中定义的自定义参数的映射。
Page Title 返回给定页面的标题。
Site Data 返回由 data 目录中的文件组成的数据结构。
Site Params 返回站点配置中定义的自定义参数的映射。
Site Title 返回站点配置中定义的标题。

使用点 (.) 将方法链接到其对象,如下所示,请记住前导点表示 当前上下文

layouts/page.html
{{ .Site.Title }} → 我的站点标题
{{ .Page.Title }} → 我的页面标题

传递给大多数模板的上下文是 Page 对象,因此这等同于上一个示例:

layouts/page.html
{{ .Site.Title }} → 我的站点标题
{{ .Title }} → 我的页面标题

某些方法接受参数。用空格将参数与方法分开。例如:

layouts/page.html
{{ $page := .Page.GetPage "/books/les-miserables" }}
{{ $page.Title }} → 悲惨世界

注释

不要尝试使用 HTML 注释分隔符来注释模板代码。

Hugo 在渲染页面时会删除 HTML 注释,但首先会评估 HTML 注释分隔符内的任何模板代码。根据 HTML 注释分隔符内的模板代码,这可能会导致意外结果或导致构建失败。

模板注释类似于模板动作。成对的开始和结束大括号表示注释的开始和结束。例如:

{{/* 这是一个内联注释。*/}}
{{- /* 这是一个删除相邻空白的内联注释。*/ -}}

注释内的代码不会被解析、执行或显示。注释可以是内联的,如上所示,也可以是块形式的:

{{/*
这是一个块注释。
*/}}

{{- /*
这是一个删除相邻空白的
块注释。
*/ -}}

您不能将一个注释嵌套在另一个注释内。

要渲染 HTML 注释,请将字符串传递给 safeHTML 模板函数。例如:

{{ "<!-- 我是一个 HTML 注释。-->" | safeHTML }}
{{ printf "<!-- 这是 %s 站点。-->" .Site.Title | safeHTML }}

包含

使用 template 函数包含一个或多个 Hugo 的 嵌入式模板

{{ partial "google_analytics.html" . }}
{{ partial "opengraph" . }}
{{ partial "pagination.html" . }}
{{ partial "schema.html" . }}
{{ partial "twitter_cards.html" . }}

使用 partialpartialCached 函数包含一个或多个 部分模板

{{ partial "breadcrumbs.html" . }}
{{ partialCached "css.html" . }}

layouts/_partials 目录中创建您的 部分 模板。

在上面的示例中,请注意我们将当前上下文(点)传递给每个模板。

示例

这组简化的示例展示了上述一些概念。有关具体示例,请参阅 函数方法模板 文档。

条件块

请参阅 ifelseend 的文档。

{{ $var := 42 }}
{{ if eq $var 6 }}
  {{ print "var 是 6" }}
{{ else if eq $var 7 }}
  {{ print "var 是 7" }}
{{ else if eq $var 42 }}
  {{ print "var 是 42" }}
{{ else }}
  {{ print "var 是其他值" }}
{{ end }}

逻辑运算符

请参阅 andor 的文档。

{{ $v1 := true }}
{{ $v2 := false }}
{{ $v3 := false }}
{{ $result := false }}

{{ if and $v1 $v2 $v3 }}
  {{ $result = true }}
{{ end }}
{{ $result }} → false

{{ if or $v1 $v2 $v3 }}
  {{ $result = true }}
{{ end }}
{{ $result }} → true

循环

请参阅 rangeelseend 的文档。

{{ $s := slice "foo" "bar" "baz" }}
{{ range $s }}
  <p>{{ . }}</p>
{{ else }}
  <p>集合为空</p>
{{ end }}

要循环指定次数:

{{ $s := slice }}
{{ range 3 }}
  {{ $s = $s | append . }}
{{ end }}
{{ $s }} → [0 1 2]

重新绑定上下文

请参阅 withelseend 的文档。

{{ $var := "foo" }}
{{ with $var }}
  {{ . }} → foo
{{ else }}
  {{ print "var 是假值" }}
{{ end }}

要测试多个条件:

{{ $v1 := 0 }}
{{ $v2 := 42 }}
{{ with $v1 }}
  {{ . }}
{{ else with $v2 }}
  {{ . }} → 42
{{ else }}
  {{ print "v1 和 v2 是假值" }}
{{ end }}

访问站点参数

请参阅 Site 对象上的 Params 方法文档。

使用此站点配置:

baseURL: https://example.org
params:
  author:
    email: jsmith@example.org
    name: John Smith
  copyright-year: '2023'
  layouts:
    rfc_1123: Mon, 02 Jan 2006 15:04:05 MST
    rfc_3339: '2006-01-02T15:04:05-07:00'
  subtitle: 地球上最好的 Widgets
title: ABC Widgets
baseURL = 'https://example.org'
title = 'ABC Widgets'
[params]
  copyright-year = '2023'
  subtitle = '地球上最好的 Widgets'
  [params.author]
    email = 'jsmith@example.org'
    name = 'John Smith'
  [params.layouts]
    rfc_1123 = 'Mon, 02 Jan 2006 15:04:05 MST'
    rfc_3339 = '2006-01-02T15:04:05-07:00'
{
   "baseURL": "https://example.org",
   "params": {
      "author": {
         "email": "jsmith@example.org",
         "name": "John Smith"
      },
      "copyright-year": "2023",
      "layouts": {
         "rfc_1123": "Mon, 02 Jan 2006 15:04:05 MST",
         "rfc_3339": "2006-01-02T15:04:05-07:00"
      },
      "subtitle": "地球上最好的 Widgets"
   },
   "title": "ABC Widgets"
}

通过链接标识符访问自定义站点参数:

{{ .Site.Params.subtitle }} → 地球上最好的 Widgets
{{ .Site.Params.author.name }} → John Smith

{{ $layout := .Site.Params.layouts.rfc_1123 }}
{{ .Site.Lastmod.Format $layout }} → Tue, 17 Oct 2023 13:21:02 PDT

访问页面参数

请参阅 Page 对象上的 Params 方法文档。

举例来说,考虑此前言:

---
date: 2023-10-17T15:11:37-07:00
params:
  author:
    email: jsmith@example.org
    name: John Smith
  display_related: true
  key-with-hyphens: 必须使用 index 函数
title: 年度会议
---
+++
date = 2023-10-17T15:11:37-07:00
title = '年度会议'
[params]
  display_related = true
  key-with-hyphens = '必须使用 index 函数'
  [params.author]
    email = 'jsmith@example.org'
    name = 'John Smith'
+++
{
   "date": "2023-10-17T15:11:37-07:00",
   "params": {
      "author": {
         "email": "jsmith@example.org",
         "name": "John Smith"
      },
      "display_related": true,
      "key-with-hyphens": "必须使用 index 函数"
   },
   "title": "年度会议"
}

titledate 字段是标准 前言字段,而其他字段是用户定义的。

通过 链接 标识符 访问自定义字段(在需要时):

{{ .Params.display_related }} → true
{{ .Params.author.email }} → jsmith@example.org
{{ .Params.author.name }} → John Smith

在上面的模板示例中,每个键都是有效的标识符。例如,没有键包含连字符。要访问不是有效标识符的键,请使用 index 函数:

{{ index .Params "key-with-hyphens" }} → 必须使用 index 函数

Last updated: January 1, 0001
Improve this page