1 - Hugo模板入门介绍

Introduction to Hugo Templating - Hugo模板入门介绍

https://gohugo.io/templates/introduction/

​ Hugo 使用 Go 的 html/templatetext/template 库作为模板的基础。

​ 以下仅为 Go 模板的入门指南。若想深入了解 Go 模板,请查看官方 Go 文档

​ Go 模板提供了一种极其简单的模板语言,坚信只有最基本的逻辑应该放在模板或视图层中。

基本语法

​ Go 模板是 HTML 文件,并加入了变量函数。Go 模板的变量和函数可以在 {{ }}中进行访问。

访问预定义变量

​ 预定义变量可以是当前作用域中已经存在的变量(如下面变量章节.Title示例),也可以是自定义变量(如该章节中的$address示例)。

1
2
{{ .Title }}
{{ $address }}

​ 函数的参数使用空格分隔。通常的语法如下:

1
{{ FUNCTION ARG1 ARG2 .. }}

​ 下面的示例使用12作为add函数的输入:

1
{{ add 1 2 }}

通过点符号访问方法和字段

​ 访问在内容前置元数据中定义的 Page 参数中的 bar

1
{{ .Params.bar }}

括号可以用来分组项

1
{{ if or (isset .Params "alt") (isset .Params "caption") }} Caption {{ end }}

单个语句可以分成多行

1
2
3
4
{{ if or
  (isset .Params "alt")
  (isset .Params "caption")
}}

原始字符串字面值可以包含换行符

1
2
{{ $msg := `Line one.
Line two.` }}

变量

​ 每个Go模板都有一个数据对象。在Hugo中,每个模板都传递了一个Page。在下面的示例中,.TitlePage变量中可访问的元素之一。

​ 由于Page是模板的默认作用域,因此可以通过点前缀(.Title)轻松访问当前作用域(. —— “the dot")中的Title元素:

1
<title>{{ .Title }}</title>

​ 值也可以存储在自定义变量中,并在之后被引用:

​ 自定义变量需要以$为前缀。

1
2
{{ $address := "123 Main St." }}
{{ $address }}

​ 使用=运算符可以重新定义变量。下面的示例在主页上打印"Var is Hugo Home”,在其他所有页面上打印"Var is Hugo Page":

1
2
3
4
5
{{ $var := "Hugo Page" }}
{{ if .IsHome }}
    {{ $var = "Hugo Home" }}
{{ end }}
Var is {{ $var }}

函数

​ Go模板仅提供了一些基本函数,但还提供了一种机制,使应用程序能够扩展原始函数集。

Hugo模板函数提供了特定于构建站点的附加功能。通过使用函数名和由空格分隔的所需参数来调用函数。如果没有重新编译Hugo,则无法添加模板函数。

示例1:添加数字

1
2
{{ add 1 2 }}
<!-- prints 3 -->

示例2:比较数字

1
2
{{ lt 1 2 }}
<!-- prints true (i.e., since 1 is less than 2) -->

​ 请注意,这两个示例都使用了Go模板的math函数。

​ 在Go模板文档中,有比Hugo文档中列出的更多的布尔运算符。

Includes

​ 在包含(Include)另一个模板时,您需要传递它需要访问的数据。

​ 为了传递当前上下文,请记住包含一个尾随点。

​ 模板位置始终从Hugo的layouts/目录开始。

Partial

partial函数用于使用语法{{ partial "<PATH>/<PARTIAL>.<EXTENSION>" . }}包含部分(partial)模板。

​ 包含layouts/partials/header.html部分(partial)模板的示例:

1
{{ partial "header.html" . }}

模板

​ 在早期版本的Hugo中,template函数用于包含部分(partial)模板。现在,它仅用于调用内部模板。语法为{{ template "_internal/<TEMPLATE>.<EXTENSION>" . }}

​ 可以在这里找到可用的内部模板。

​ 包含内部opengraph.html模板的示例:

1
{{ template "_internal/opengraph.html" . }}

逻辑

​ Go模板提供了最基本的迭代和条件逻辑。

迭代

​ Go模板大量使用range来迭代map、array或slice。以下是如何使用range的不同示例。

示例1:使用上下文(.)

1
2
3
{{ range $array }}
    {{ . }} <!-- The . represents an element in $array -->
{{ end }}

示例2:为数组元素的值声明变量名

1
2
3
{{ range $elem_val := $array }}
    {{ $elem_val }}
{{ end }}

示例3:为数组元素的索引和值声明变量名

​ 对于数组或切片,第一个声明的变量将映射到每个元素的索引。

1
2
3
{{ range $elem_index, $elem_val := $array }}
   {{ $elem_index }} -- {{ $elem_val }}
{{ end }}

示例4:为map元素的键和值声明变量名

​ 对于map,第一个声明的变量将映射到每个map元素的键。

1
2
3
{{ range $elem_key, $elem_val := $map }}
   {{ $elem_key }} -- {{ $elem_val }}
{{ end }}

示例5:针对空map、数组或切片的条件语句

​ 如果传递给range的map、数组或切片长度为零,则将执行else语句。

1
2
3
4
5
{{ range $array }}
    {{ . }}
{{ else }}
    <!-- This is only evaluated if $array is empty -->
{{ end }}

条件语句

ifelsewithorandnot提供了处理Go模板中条件逻辑的框架。与range一样,ifwith语句也是用{{ end }}关闭的。

​ Go 模板将以下值视为false

  • false (boolean)
  • 0 (integer)
  • 任何长度为零的数组、切片、映射或字符串

示例1:with

​ 通常使用with编写"if something exists, do this"这样的语句。

with会在其作用域内重新绑定上下文(.)(就像在range中一样)。

​ 如果变量不存在,或者如果它按上面所述计算为"false",则它会跳过该块。

1
2
3
{{ with .Params.title }}
    <h4>{{ . }}</h4>
{{ end }}

示例2:with..else

​ 下面的代码片段如果设置了"description"前置元数据的值,则使用它,否则使用默认的.Summary页面变量

1
2
3
4
5
{{ with .Param "description" }}
    {{ . }}
{{ else }}
    {{ .Summary }}
{{ end }}

​ 参见.Param函数

示例3:if

​ 编写with的另一种替代(更冗长)方法是使用if。在这里,.不会重新绑定。

​ 下面的示例是使用if重写的"示例1":

1
2
3
{{ if isset .Params "title" }}
    <h4>{{ index .Params "title" }}</h4>
{{ end }}

示例4:if..else

​ 下面的示例是使用if .. else重写的"示例2",并使用isset函数+ .Params变量(不同于.Param函数):

1
2
3
4
5
{{ if (isset .Params "description") }}
    {{ index .Params "description" }}
{{ else }}
    {{ .Summary }}
{{ end }}

示例5: if .. else if .. else

​ 与with不同,if还可以包含else if子句。

1
2
3
4
5
6
7
{{ if (isset .Params "description") }}
    {{ index .Params "description" }}
{{ else if (isset .Params "summary") }}
    {{ index .Params "summary" }}
{{ else }}
    {{ .Summary }}
{{ end }}

示例6: and & or

1
{{ if (and (or (isset .Params "title") (isset .Params "caption")) (isset .Params "attr")) }}

管道

​ Go模板最强大的组件之一是能够将操作一层层堆叠在一起。这是通过使用管道来完成的。从Unix管道借鉴而来,概念很简单:每个管道的输出都成为下一个管道的输入。

​ 由于Go模板的语法非常简单,因此管道对于能够链接在一起的函数调用非常重要。管道的一个限制是它们只能处理单个值,该值成为下一个管道的最后一个参数。

​ 一些简单的示例应该有助于帮助您了解如何使用管道。

示例1: shuffle

​ 以下两个示例在功能上是相同的:

1
2
{{ shuffle (seq 1 5) }}
{{ (seq 1 5) | shuffle }}

示例2: index

​ 以下访问名为"disqus_url"的页面参数并转义HTML。此示例还使用内置于Go模板中的index函数

1
{{ index .Params "disqus_url" | html }}

示例3: 带有issetor

1
2
3
{{ if or (or (isset .Params "title") (isset .Params "caption")) (isset .Params "attr") }}
Stuff Here
{{ end }}

可以重写为

1
2
3
{{ if isset .Params "caption" | or isset .Params "title" | or isset .Params "attr" }}
Stuff Here
{{ end }}

上下文(也称"点")

​ Go模板最容易被忽视的理解概念是,{{ . }}始终指向当前上下文。

  • 在您的模板的顶层,它将是可用的数据集。
  • 但是,在迭代中,它将具有循环中当前项的值;即{{ . }}将不再引用整个页面可用的数据。

​ 如果您需要从循环内部访问页面级别的数据(例如,在前置元数据中设置的页面参数),则可能需要执行以下操作之一:

1.定义一个独立于上下文的变量

​ 以下示例演示如何定义一个独立于上下文的变量。

tags-range-with-page-variable.html

1
2
3
4
5
6
7
8
9
{{ $title := .Site.Title }}
<ul>
{{ range .Params.tags }}
    <li>
        <a href="/tags/{{ . | urlize }}">{{ . }}</a>
        - {{ $title }}
    </li>
{{ end }}
</ul>

请注意,一旦我们进入循环(即 range),{{ . }} 的值就已经改变了。我们在循环外面定义了一个变量({{ $title }}),并为其分配了一个值,以便我们可以从循环内部访问该值。

2.使用$.访问全局上下文

$ 在模板中具有特殊意义。$默认情况下被设置为 .(“the dot”)的起始值。这是Go text/template的文档功能。这意味着您可以从任何地方访问全局上下文。下面是先前的代码块的等效示例,但现在使用 $ 从全局上下文中获取 .Site.Title

range-through-tags-w-global.html

1
2
3
4
5
6
7
8
<ul>
{{ range .Params.tags }}
  <li>
    <a href="/tags/{{ . | urlize }}">{{ . }}</a>
            - {{ $.Site.Title }}
  </li>
{{ end }}
</ul>

​ 如果有人恶意重新定义特殊字符(例如 {{ $ := .Site }}),$ 的内置魔力将停止工作。不要这样做。当然,您可以在全局上下文中使用 {{ $ := . }} 来重置 $ 的默认值以恢复其功能。

空格

​ Go 1.6可以通过在相应的 {{}}分隔符旁边包含连字符(-)和空格来修剪Go标记的两侧的空格的功能。

​ 例如,以下Go模板将在其HTML输出中包含换行符和水平制表符:

1
2
3
<div>
  {{ .Title }}
</div>

它将输出:

1
2
3
<div>
  Hello, World!
</div>

​ 在以下示例中使用-将删除围绕.Title变量的额外空格并删除换行符:

1
2
3
<div>
  {{- .Title -}}
</div>

它将输出:

1
<div>Hello, World!</div>

Go 语言认为以下字符是空白字符:

  • 空格
  • 水平制表符
  • 回车符
  • 换行符

注释

​ 为了使您的模板组织有序并在团队之间共享信息,您可能希望向您的模板添加注释。在Hugo中有两种方法可以做到这一点。

Go模板注释

​ Go模板支持{{/**/}}来打开和关闭注释块。该块内的任何内容都不会被渲染。

例如:

1
Bonsoir, {{/* {{ add 0 + 2 }} */}}Eliott.

将渲染Bonsoir, Eliott.,而不关心注释块中的语法错误(add 0 + 2)。

HTML 注释

​ 您可以通过将 HTML 代码注释的字符串管道化到 safeHTML 中来添加 HTML 注释。

例如:

1
{{ "<!-- This is an HTML comment -->" | safeHTML }}

​ 如果您需要使用变量构造这样的HTML注释,只需将printf管道化到safeHTML

例如:

1
{{ printf "<!-- Our website is named: %s -->" .Site.Title | safeHTML }}

包含 Go 模板的 HTML 注释

​ 默认情况下,HTML注释会被删除,但其内容仍将被求值。这意味着尽管HTML注释永远不会将任何内容渲染到最终的HTML页面,但其中包含的代码可能会导致构建过程失败。

​ 不要尝试使用HTML注释来注释掉Go模板代码。

1
2
<!-- {{ $author := "Emma Goldman" }} was a great woman. -->
{{ $author }}

​ 模板引擎将删除HTML注释中的内容,但如果其中存在Go模板代码,则将首先求值任何Go模板代码。因此,上面的示例将渲染成Emma Goldman,因为$author变量在HTML注释中得到求值。但是,如果HTML注释中的代码有错误,构建将会失败。

Hugo参数

​ Hugo 提供了通过站点配置(用于整个站点的值)或每个特定内容的元数据(即前置元数据)向模板层传递值的选项。您可以定义任何类型的任何值,并在模板中任意使用它们,只要这些值得到前置元数据格式支持。

使用内容(page)参数

​ 您可以在单个内容的前置元数据中提供变量以供模板使用。

​ Hugo 文档中使用了一个示例。大多数页面都受益于提供目录,但有时目录并不合适。我们在前置元数据中定义了一个 notoc 变量,当设置为 true 时,将防止目录呈现。

以下是示例前置元数据:

content/example.md

=== “yaml”

``` yaml
---
notoc: true
title: Example
---
```

=== “toml”

``` toml
+++
notoc = true
title = 'Example'
+++
```

=== “json”

``` json
{
   "notoc": true,
   "title": "Example"
}
```

​ 以下是可以在 toc.html 局部模板中使用的对应代码示例:

layouts/partials/toc.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
{{ if not .Params.notoc }}
<aside>
  <header>
    <a href="#{{ .Title | urlize }}">
    <h3>{{ .Title }}</h3>
    </a>
  </header>
  {{ .TableOfContents }}
</aside>
<a href="#" id="toc-toggle"></a>
{{ end }}

​ 我们希望页面的默认行为是包含目录,除非另有指定。此模板检查此页面前置元数据中的 notoc: 字段是否为 true

使用站点配置参数

​ 您可以在站点配置文件中任意定义任何数量的站点级参数。这些参数在您的模板中全局可用。

​ 例如,您可以声明以下内容:

config.

=== “yaml”

``` yaml
params:
  copyrighthtml: Copyright &#xA9; 2017 John Doe. All Rights Reserved.
  sidebarrecentlimit: 5
  twitteruser: spf13
```

=== “toml”

``` toml
[params]
  copyrighthtml = 'Copyright &#xA9; 2017 John Doe. All Rights Reserved.'
  sidebarrecentlimit = 5
  twitteruser = 'spf13'
```

=== “json”

``` json
{
   "params": {
      "copyrighthtml": "Copyright \u0026#xA9; 2017 John Doe. All Rights Reserved.",
      "sidebarrecentlimit": 5,
      "twitteruser": "spf13"
   }
}
```

​ 在页脚布局中,您可以声明仅在提供了 copyrighthtml 参数时才呈现的 <footer>。如果提供了该参数,则需要通过 safeHTML 函数声明该字符串可以安全使用,以便 HTML 实体不会被再次转义。这使您可以轻松地每年 1 月 1 日仅更新顶级配置文件,而无需在模板中查找。

1
2
3
4
5
{{ if .Site.Params.copyrighthtml }}
    <footer>
        <div class="text-center">{{ .Site.Params.CopyrightHTML | safeHTML }}</div>
    </footer>
{{ end }}

​ 一种替代写"if“并引用同一值的方法是使用withwith在其作用域内重新绑定上下文(.),如果该变量不存在,则跳过块:

layouts/partials/twitter.html

1
2
3
4
5
6
{{ with .Site.Params.twitteruser }}
    <div>
        <a href="https://twitter.com/{{ . }}" rel="author">
        <img src="/images/twitter.png" width="48" height="48" title="Twitter: {{ . }}" alt="Twitter"></a>
    </div>
{{ end }}

​ 最后,您也可以将"魔术常量"从您的布局中拉出来。以下示例使用first函数,以及.RelPermalink页面变量和.Site.Pages站点变量。

1
2
3
4
5
6
7
8
<nav>
  <h1>Recent Posts</h1>
  <ul>
  {{- range first .Site.Params.SidebarRecentLimit .Site.Pages -}}
      <li><a href="{{ .RelPermalink }}">{{ .Title }}</a></li>
  {{- end -}}
  </ul>
</nav>    

示例:显示未来事件

​ 假设有以下内容结构和前置元数据:

1
2
3
4
5
content/
└── events/
    ├── event-1.md
    ├── event-2.md
    └── event-3.md

content/events/event-1.md.

=== “yaml”

``` yaml
date: 2021-12-06T10:37:16-08:00
draft: false
end_date: 2021-12-05T11:00:00-08:00
start_date: 2021-12-05T09:00:00-08:00
title: Event 1
```

=== “toml”

``` toml
date = 2021-12-06T10:37:16-08:00
draft = false
end_date = 2021-12-05T11:00:00-08:00
start_date = 2021-12-05T09:00:00-08:00
title = 'Event 1'
```

=== “json”

``` json
{
   "date": "2021-12-06T10:37:16-08:00",
   "draft": false,
   "end_date": "2021-12-05T11:00:00-08:00",
   "start_date": "2021-12-05T09:00:00-08:00",
   "title": "Event 1"
}
```

​ 这个局部模板渲染未来的事件:

layouts/partials/future-events.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<h2>Future Events</h2>
<ul>
  {{ range where site.RegularPages "Type" "events" }}
    {{ if gt (.Params.start_date | time.AsTime) now }}
      {{ $startDate := .Params.start_date | time.Format ":date_medium" }}
      <li>
        <a href="{{ .RelPermalink }}">{{ .LinkTitle }}</a> - {{ $startDate }}
      </li>
    {{ end }}
  {{ end }}
</ul>

​ 如果将前置元数据限制为TOML格式,并省略日期字段周围的引号,则可以执行日期比较而无需强制转换。

layouts/partials/future-events.html

1
2
3
4
5
6
7
8
9
<h2>Future Events</h2>
<ul>
  {{ range where (where site.RegularPages "Type" "events") "Params.start_date" "gt" now }}
    {{ $startDate := .Params.start_date | time.Format ":date_medium" }}
    <li>
      <a href="{{ .RelPermalink }}">{{ .LinkTitle }}</a> - {{ $startDate }}
    </li>
  {{ end }}
</ul>

另请参阅

2 - Hugo 的查找顺序

Hugo’s Lookup Order - Hugo 的查找顺序

https://gohugo.io/templates/lookup-order/

​ Hugo按照一定的顺序查找给定页面的布局,从最具体的布局开始。

Hugo 布局查找规则

​ Hugo在选择给定页面的布局时会考虑下面列出的参数。它们按优先级排序。这应该很自然,但具体的参数变化请参考下面的表格。

  • Kind

    页面的Kind(主页是其中之一)。请参见下面每个种类的示例表格。这也确定了它是单页面(即常规内容页面。然后我们在_default/single.html中寻找HTML模板)还是列表页面(章节列表、主页、分类列表、分类术语。然后我们在_default/list.html中寻找HTML模板)。

  • Layout

    可以在页面前置元数据中设置。

  • 输出格式

    请参见自定义输出格式。输出格式既有一个name(例如rssamphtml),也有一个suffix(例如xmlhtml)。我们更喜欢两者匹配(例如index.amp.html),但寻找较不具体的模板。

请注意,如果输出格式的媒体类型定义了多个后缀,则只考虑第一个后缀。

  • Language

    模板名称中会考虑语言代码。如果站点语言是fr,则index.fr.amp.html将优于index.amp.html,但 index.amp.html 将在 index.fr.html 之前被选择。

  • Type

    如果在前置元数据中设置了type的值,则它是type的值,否则它是根章节的名称(例如"blog")。它总是有一个值,所以如果未设置,则值为"page"。

  • Section

    对于sectiontaxonomyterm类型很重要。

提示:下面的示例看起来很长、很复杂。这就是灵活性在起作用。大多数Hugo站点只包含少量模板:

1
2
3
4
5
├── _default
│   ├── baseof.html
│   ├── list.html
│   └── single.html
└── index.html

带有主题的 Hugo 布局查找规则

​ 在Hugo中,布局可以存在于项目或主题的布局文件夹中,并且会选择最具体的布局。Hugo将交错查找下面的布局,找到最具体的一个布局,无论是在项目还是主题中。

示例:常规页面的布局查找

ExampleOutputFormatSuffixTemplate Lookup Order
Single page in “posts” sectionHTMLhtmllayouts/posts/single.html.htmllayouts/posts/single.htmllayouts/_default/single.html.htmllayouts/_default/single.html
Base template for single page in “posts” sectionHTMLhtmllayouts/posts/single-baseof.html.htmllayouts/posts/baseof.html.htmllayouts/posts/single-baseof.htmllayouts/posts/baseof.htmllayouts/_default/single-baseof.html.htmllayouts/_default/baseof.html.htmllayouts/_default/single-baseof.htmllayouts/_default/baseof.html
Single page in “posts” section with layout setHTMLhtmllayouts/posts/demolayout.html.htmllayouts/posts/single.html.htmllayouts/posts/demolayout.htmllayouts/posts/single.htmllayouts/_default/demolayout.html.htmllayouts/_default/single.html.htmllayouts/_default/demolayout.htmllayouts/_default/single.html
Base template for single page in “posts” section with layout setHTMLhtmllayouts/posts/demolayout-baseof.html.htmllayouts/posts/single-baseof.html.htmllayouts/posts/baseof.html.htmllayouts/posts/demolayout-baseof.htmllayouts/posts/single-baseof.htmllayouts/posts/baseof.htmllayouts/_default/demolayout-baseof.html.htmllayouts/_default/single-baseof.html.htmllayouts/_default/baseof.html.htmllayouts/_default/demolayout-baseof.htmllayouts/_default/single-baseof.htmllayouts/_default/baseof.html
AMP single pageAMPhtmllayouts/posts/single.amp.htmllayouts/posts/single.htmllayouts/_default/single.amp.htmllayouts/_default/single.html
AMP single page, French languageAMPhtmllayouts/posts/single.fr.amp.htmllayouts/posts/single.amp.htmllayouts/posts/single.fr.htmllayouts/posts/single.htmllayouts/_default/single.fr.amp.htmllayouts/_default/single.amp.htmllayouts/_default/single.fr.htmllayouts/_default/single.html

示例:主页的布局查找

ExampleOutputFormatSuffixTemplate Lookup Order
Home pageHTMLhtmllayouts/index.html.htmllayouts/home.html.htmllayouts/list.html.htmllayouts/index.htmllayouts/home.htmllayouts/list.htmllayouts/_default/index.html.htmllayouts/_default/home.html.htmllayouts/_default/list.html.htmllayouts/_default/index.htmllayouts/_default/home.htmllayouts/_default/list.html
Base template for home pageHTMLhtmllayouts/index-baseof.html.htmllayouts/home-baseof.html.htmllayouts/list-baseof.html.htmllayouts/baseof.html.htmllayouts/index-baseof.htmllayouts/home-baseof.htmllayouts/list-baseof.htmllayouts/baseof.htmllayouts/_default/index-baseof.html.htmllayouts/_default/home-baseof.html.htmllayouts/_default/list-baseof.html.htmllayouts/_default/baseof.html.htmllayouts/_default/index-baseof.htmllayouts/_default/home-baseof.htmllayouts/_default/list-baseof.htmllayouts/_default/baseof.html
Home page with type setHTMLhtmllayouts/demotype/index.html.htmllayouts/demotype/home.html.htmllayouts/demotype/list.html.htmllayouts/demotype/index.htmllayouts/demotype/home.htmllayouts/demotype/list.htmllayouts/index.html.htmllayouts/home.html.htmllayouts/list.html.htmllayouts/index.htmllayouts/home.htmllayouts/list.htmllayouts/_default/index.html.htmllayouts/_default/home.html.htmllayouts/_default/list.html.htmllayouts/_default/index.htmllayouts/_default/home.htmllayouts/_default/list.html
Base template for home page with type setHTMLhtmllayouts/demotype/index-baseof.html.htmllayouts/demotype/home-baseof.html.htmllayouts/demotype/list-baseof.html.htmllayouts/demotype/baseof.html.htmllayouts/demotype/index-baseof.htmllayouts/demotype/home-baseof.htmllayouts/demotype/list-baseof.htmllayouts/demotype/baseof.htmllayouts/index-baseof.html.htmllayouts/home-baseof.html.htmllayouts/list-baseof.html.htmllayouts/baseof.html.htmllayouts/index-baseof.htmllayouts/home-baseof.htmllayouts/list-baseof.htmllayouts/baseof.htmllayouts/_default/index-baseof.html.htmllayouts/_default/home-baseof.html.htmllayouts/_default/list-baseof.html.htmllayouts/_default/baseof.html.htmllayouts/_default/index-baseof.htmllayouts/_default/home-baseof.htmllayouts/_default/list-baseof.htmllayouts/_default/baseof.html
Home page with layout setHTMLhtmllayouts/demolayout.html.htmllayouts/index.html.htmllayouts/home.html.htmllayouts/list.html.htmllayouts/demolayout.htmllayouts/index.htmllayouts/home.htmllayouts/list.htmllayouts/_default/demolayout.html.htmllayouts/_default/index.html.htmllayouts/_default/home.html.htmllayouts/_default/list.html.htmllayouts/_default/demolayout.htmllayouts/_default/index.htmllayouts/_default/home.htmllayouts/_default/list.html
AMP home, French languageAMPhtmllayouts/index.fr.amp.htmllayouts/home.fr.amp.htmllayouts/list.fr.amp.htmllayouts/index.amp.htmllayouts/home.amp.htmllayouts/list.amp.htmllayouts/index.fr.htmllayouts/home.fr.htmllayouts/list.fr.htmllayouts/index.htmllayouts/home.htmllayouts/list.htmllayouts/_default/index.fr.amp.htmllayouts/_default/home.fr.amp.htmllayouts/_default/list.fr.amp.htmllayouts/_default/index.amp.htmllayouts/_default/home.amp.htmllayouts/_default/list.amp.htmllayouts/_default/index.fr.htmllayouts/_default/home.fr.htmllayouts/_default/list.fr.htmllayouts/_default/index.htmllayouts/_default/home.htmllayouts/_default/list.html
JSON homeJSONjsonlayouts/index.json.jsonlayouts/home.json.jsonlayouts/list.json.jsonlayouts/index.jsonlayouts/home.jsonlayouts/list.jsonlayouts/_default/index.json.jsonlayouts/_default/home.json.jsonlayouts/_default/list.json.jsonlayouts/_default/index.jsonlayouts/_default/home.jsonlayouts/_default/list.json
RSS homeRSSxmllayouts/index.rss.xmllayouts/home.rss.xmllayouts/rss.xmllayouts/list.rss.xmllayouts/index.xmllayouts/home.xmllayouts/list.xmllayouts/_default/index.rss.xmllayouts/_default/home.rss.xmllayouts/_default/rss.xmllayouts/_default/list.rss.xmllayouts/_default/index.xmllayouts/_default/home.xmllayouts/_default/list.xmllayouts/_internal/_default/rss.xml

示例:章节页面的布局查找

ExampleOutputFormatSuffixTemplate Lookup Order
RSS section postsRSSxmllayouts/posts/section.rss.xmllayouts/posts/rss.xmllayouts/posts/list.rss.xmllayouts/posts/section.xmllayouts/posts/list.xmllayouts/section/section.rss.xmllayouts/section/rss.xmllayouts/section/list.rss.xmllayouts/section/section.xmllayouts/section/list.xmllayouts/_default/section.rss.xmllayouts/_default/rss.xmllayouts/_default/list.rss.xmllayouts/_default/section.xmllayouts/_default/list.xmllayouts/_internal/_default/rss.xml
Section list for “posts” sectionHTMLhtmllayouts/posts/posts.html.htmllayouts/posts/section.html.htmllayouts/posts/list.html.htmllayouts/posts/posts.htmllayouts/posts/section.htmllayouts/posts/list.htmllayouts/section/posts.html.htmllayouts/section/section.html.htmllayouts/section/list.html.htmllayouts/section/posts.htmllayouts/section/section.htmllayouts/section/list.htmllayouts/_default/posts.html.htmllayouts/_default/section.html.htmllayouts/_default/list.html.htmllayouts/_default/posts.htmllayouts/_default/section.htmllayouts/_default/list.html
Section list for “posts” section with type set to “blog”HTMLhtmllayouts/blog/posts.html.htmllayouts/blog/section.html.htmllayouts/blog/list.html.htmllayouts/blog/posts.htmllayouts/blog/section.htmllayouts/blog/list.htmllayouts/posts/posts.html.htmllayouts/posts/section.html.htmllayouts/posts/list.html.htmllayouts/posts/posts.htmllayouts/posts/section.htmllayouts/posts/list.htmllayouts/section/posts.html.htmllayouts/section/section.html.htmllayouts/section/list.html.htmllayouts/section/posts.htmllayouts/section/section.htmllayouts/section/list.htmllayouts/_default/posts.html.htmllayouts/_default/section.html.htmllayouts/_default/list.html.htmllayouts/_default/posts.htmllayouts/_default/section.htmllayouts/_default/list.html
Section list for “posts” section with layout set to “demoLayout”HTMLhtmllayouts/posts/demolayout.html.htmllayouts/posts/posts.html.htmllayouts/posts/section.html.htmllayouts/posts/list.html.htmllayouts/posts/demolayout.htmllayouts/posts/posts.htmllayouts/posts/section.htmllayouts/posts/list.htmllayouts/section/demolayout.html.htmllayouts/section/posts.html.htmllayouts/section/section.html.htmllayouts/section/list.html.htmllayouts/section/demolayout.htmllayouts/section/posts.htmllayouts/section/section.htmllayouts/section/list.htmllayouts/_default/demolayout.html.htmllayouts/_default/posts.html.htmllayouts/_default/section.html.htmllayouts/_default/list.html.htmllayouts/_default/demolayout.htmllayouts/_default/posts.htmllayouts/_default/section.htmllayouts/_default/list.html

示例:分类页面的布局查找

ExampleOutputFormatSuffixTemplate Lookup Order
Taxonomy in categoriesRSSxmllayouts/categories/category.terms.rss.xmllayouts/categories/terms.rss.xmllayouts/categories/taxonomy.rss.xmllayouts/categories/rss.xmllayouts/categories/list.rss.xmllayouts/categories/category.terms.xmllayouts/categories/terms.xmllayouts/categories/taxonomy.xmllayouts/categories/list.xmllayouts/category/category.terms.rss.xmllayouts/category/terms.rss.xmllayouts/category/taxonomy.rss.xmllayouts/category/rss.xmllayouts/category/list.rss.xmllayouts/category/category.terms.xmllayouts/category/terms.xmllayouts/category/taxonomy.xmllayouts/category/list.xmllayouts/taxonomy/category.terms.rss.xmllayouts/taxonomy/terms.rss.xmllayouts/taxonomy/taxonomy.rss.xmllayouts/taxonomy/rss.xmllayouts/taxonomy/list.rss.xmllayouts/taxonomy/category.terms.xmllayouts/taxonomy/terms.xmllayouts/taxonomy/taxonomy.xmllayouts/taxonomy/list.xmllayouts/_default/category.terms.rss.xmllayouts/_default/terms.rss.xmllayouts/_default/taxonomy.rss.xmllayouts/_default/rss.xmllayouts/_default/list.rss.xmllayouts/_default/category.terms.xmllayouts/_default/terms.xmllayouts/_default/taxonomy.xmllayouts/_default/list.xmllayouts/_internal/_default/rss.xml
Taxonomy list in categoriesHTMLhtmllayouts/categories/category.terms.html.htmllayouts/categories/terms.html.htmllayouts/categories/taxonomy.html.htmllayouts/categories/list.html.htmllayouts/categories/category.terms.htmllayouts/categories/terms.htmllayouts/categories/taxonomy.htmllayouts/categories/list.htmllayouts/category/category.terms.html.htmllayouts/category/terms.html.htmllayouts/category/taxonomy.html.htmllayouts/category/list.html.htmllayouts/category/category.terms.htmllayouts/category/terms.htmllayouts/category/taxonomy.htmllayouts/category/list.htmllayouts/taxonomy/category.terms.html.htmllayouts/taxonomy/terms.html.htmllayouts/taxonomy/taxonomy.html.htmllayouts/taxonomy/list.html.htmllayouts/taxonomy/category.terms.htmllayouts/taxonomy/terms.htmllayouts/taxonomy/taxonomy.htmllayouts/taxonomy/list.htmllayouts/_default/category.terms.html.htmllayouts/_default/terms.html.htmllayouts/_default/taxonomy.html.htmllayouts/_default/list.html.htmllayouts/_default/category.terms.htmllayouts/_default/terms.htmllayouts/_default/taxonomy.htmllayouts/_default/list.html

示例:术语页面的布局查找

ExampleOutputFormatSuffixTemplate Lookup Order
Term in categoriesRSSxmllayouts/categories/term.rss.xmllayouts/categories/category.rss.xmllayouts/categories/taxonomy.rss.xmllayouts/categories/rss.xmllayouts/categories/list.rss.xmllayouts/categories/term.xmllayouts/categories/category.xmllayouts/categories/taxonomy.xmllayouts/categories/list.xmllayouts/term/term.rss.xmllayouts/term/category.rss.xmllayouts/term/taxonomy.rss.xmllayouts/term/rss.xmllayouts/term/list.rss.xmllayouts/term/term.xmllayouts/term/category.xmllayouts/term/taxonomy.xmllayouts/term/list.xmllayouts/taxonomy/term.rss.xmllayouts/taxonomy/category.rss.xmllayouts/taxonomy/taxonomy.rss.xmllayouts/taxonomy/rss.xmllayouts/taxonomy/list.rss.xmllayouts/taxonomy/term.xmllayouts/taxonomy/category.xmllayouts/taxonomy/taxonomy.xmllayouts/taxonomy/list.xmllayouts/category/term.rss.xmllayouts/category/category.rss.xmllayouts/category/taxonomy.rss.xmllayouts/category/rss.xmllayouts/category/list.rss.xmllayouts/category/term.xmllayouts/category/category.xmllayouts/category/taxonomy.xmllayouts/category/list.xmllayouts/_default/term.rss.xmllayouts/_default/category.rss.xmllayouts/_default/taxonomy.rss.xmllayouts/_default/rss.xmllayouts/_default/list.rss.xmllayouts/_default/term.xmllayouts/_default/category.xmllayouts/_default/taxonomy.xmllayouts/_default/list.xmllayouts/_internal/_default/rss.xml
Taxonomy term in categoriesHTMLhtmllayouts/categories/term.html.htmllayouts/categories/category.html.htmllayouts/categories/taxonomy.html.htmllayouts/categories/list.html.htmllayouts/categories/term.htmllayouts/categories/category.htmllayouts/categories/taxonomy.htmllayouts/categories/list.htmllayouts/term/term.html.htmllayouts/term/category.html.htmllayouts/term/taxonomy.html.htmllayouts/term/list.html.htmllayouts/term/term.htmllayouts/term/category.htmllayouts/term/taxonomy.htmllayouts/term/list.htmllayouts/taxonomy/term.html.htmllayouts/taxonomy/category.html.htmllayouts/taxonomy/taxonomy.html.htmllayouts/taxonomy/list.html.htmllayouts/taxonomy/term.htmllayouts/taxonomy/category.htmllayouts/taxonomy/taxonomy.htmllayouts/taxonomy/list.htmllayouts/category/term.html.htmllayouts/category/category.html.htmllayouts/category/taxonomy.html.htmllayouts/category/list.html.htmllayouts/category/term.htmllayouts/category/category.htmllayouts/category/taxonomy.htmllayouts/category/list.htmllayouts/_default/term.html.htmllayouts/_default/category.html.htmllayouts/_default/taxonomy.html.htmllayouts/_default/list.html.htmllayouts/_default/term.htmllayouts/_default/category.htmllayouts/_default/taxonomy.htmllayouts/_default/list.html

另请参阅

3 - 自定义输出格式

Custom Output Formats - 自定义输出格式

https://gohugo.io/templates/output-formats/

​ Hugo可以将内容输出为多种格式,包括日历事件、电子书格式、Google AMP和JSON搜索索引,或任何自定义文本格式。

​ 本页介绍了如何正确配置站点的媒体类型和输出格式,以及如何为自定义输出创建模板。

媒体类型

媒体类型(也称为MIME类型和内容类型)是用于在互联网上传输文件格式和格式内容的两部分标识符。

​ 这是Hugo中的完整内置媒体类型集:

typesuffixes
application/json[json]
application/manifest+json[webmanifest]
application/octet-stream[]
application/pdf[pdf]
application/rss+xml[xml rss]
application/toml[toml]
application/xml[xml]
application/yaml[yaml yml]
font/otf[otf]
font/ttf[ttf]
image/bmp[bmp]
image/gif[gif]
image/jpeg[jpg jpeg jpe jif jfif]
image/png[png]
image/svg+xml[svg]
image/webp[webp]
text/calendar[ics]
text/css[css]
text/csv[csv]
text/html[html]
text/javascript[js jsm mjs]
text/jsx[jsx]
text/markdown[md markdown]
text/plain[txt]
text/tsx[tsx]
text/typescript[ts]
text/x-sass[sass]
text/x-scss[scss]
video/3gpp[3gpp 3gp]
video/mp4[mp4]
video/mpeg[mpg mpeg]
video/ogg[ogv]
video/webm[webm]
video/x-msvideo[avi]

注意:

  • 可以添加自定义媒体类型或更改默认值;例如,如果您想将text/html的后缀更改为asp
  • Suffixes是在Hugo中用于该媒体类型的URL和文件名的值。
  • Type是定义新/自定义Output Formats时必须使用的标识符(见下文)。
  • 完整的媒体类型集将在Hugo的内置开发服务器中注册,以确保它们被浏览器识别。

​ 要添加或修改媒体类型,请在站点配置中的mediaTypes部分中定义它,可以为所有站点或特定语言定义。

config.

=== “yaml”

``` yaml
mediaTypes:
  text/enriched:
    suffixes:
    - enr
  text/html:
    suffixes:
    - asp
```

=== “toml”

``` toml
[mediaTypes]
  [mediaTypes.'text/enriched']
    suffixes = ['enr']
  [mediaTypes.'text/html']
    suffixes = ['asp']
```

=== “json”

``` json
{
   "mediaTypes": {
      "text/enriched": {
         "suffixes": [
            "enr"
         ]
      },
      "text/html": {
         "suffixes": [
            "asp"
         ]
      }
   }
}
```

​ 上面的示例添加了一个新的媒体类型text/enriched,并更改了内置的text/html媒体类型的后缀。

注意:这些媒体类型是针对您的输出格式进行配置的。如果要重新定义Hugo的默认输出格式(例如HTML),还需要重新定义媒体类型。因此,如果要将HTML输出格式的后缀从html(默认)更改为htm

config.

=== “yaml”

``` yaml
mediaTypes:
  text/html:
    suffixes:
    - htm
outputFormats:
  HTML:
    mediaType: text/html
```

=== “toml”

``` toml
[mediaTypes]
  [mediaTypes.'text/html']
    suffixes = ['htm']
[outputFormats]
  [outputFormats.HTML]
    mediaType = 'text/html'
```

=== “json”

``` json
{
   "mediaTypes": {
      "text/html": {
         "suffixes": [
            "htm"
         ]
      }
   },
   "outputFormats": {
      "HTML": {
         "mediaType": "text/html"
      }
   }
}
```

注意,要让上述内容生效,您还需要在站点配置中添加 outputs 定义。

输出格式定义

​ 给定一个媒体类型和一些其他配置,您可以获得一个输出格式。

​ 这是Hugo的所有内置输出格式:

namemediaTypepathbaseNamerelprotocolisPlainTextisHTMLnoUglypermalinkable
HTMLtext/htmlindexcanonicalfalsetruefalsetrue
AMPtext/htmlampindexamphtmlfalsetruefalsetrue
CSStext/cssstylesstylesheettruefalsefalsefalse
CSVtext/csvindexalternatetruefalsefalsefalse
Calendartext/calendarindexalternatewebcal://truefalsefalsefalse
JSONapplication/jsonindexalternatetruefalsefalsefalse
MARKDOWNtext/markdownindexalternatetruefalsefalsefalse
ROBOTStext/plainrobotsalternatetruefalsefalsefalse
RSSapplication/rss+xmlindexalternatefalsefalsetruefalse
Sitemapapplication/xmlsitemapsitemapfalsefalsetruefalse
WebAppManifestapplication/manifest+jsonmanifestmanifesttruefalsefalsefalse
  • 一个页面可以按您想要的多种输出格式输出,只要它们在文件系统上解析为唯一路径即可定义无限数量的输出格式。在上面的表格中,最好的例子是AMP vs HTMLAMPPath值为amp,因此不会覆盖HTML版本。例如,我们现在可以同时拥有 /index.html/amp/index.html
  • MediaType 必须匹配已定义媒体类型的Type
  • 您可以定义新的输出格式或重新定义内置的输出格式;例如,如果您想将AMP页面放在不同的路径中。

​ 要添加或修改输出格式,请在站点配置文件中的 outputFormats 部分中定义它,无论是为所有站点还是为给定的语言。

config.

=== “yaml”

``` yaml
outputFormats:
  MyEnrichedFormat:
    baseName: myindex
    isPlainText: true
    mediaType: text/enriched
    protocol: bep://
```

=== “toml”

``` toml
[outputFormats]
  [outputFormats.MyEnrichedFormat]
    baseName = 'myindex'
    isPlainText = true
    mediaType = 'text/enriched'
    protocol = 'bep://'
```

=== “json”

``` json
{
   "outputFormats": {
      "MyEnrichedFormat": {
         "baseName": "myindex",
         "isPlainText": true,
         "mediaType": "text/enriched",
         "protocol": "bep://"
      }
   }
}
```

​ 上述示例是虚构的,但如果用于具有 baseURL https://example.org 的站点的主页,它将产生一个具有 URL bep://example.org/myindex.enr 的纯文本主页。

配置输出格式

​ 以下是输出格式的完整配置选项列表及其默认值:

  • name

    输出格式标识符。用于定义您页面的输出格式。

  • mediaType

    这必须与已定义的媒体类型的Type匹配。

  • path

    保存输出文件的子路径。

  • baseName

    用于列表文件名(主页等)的基本文件名。默认值:index

  • rel

    可用于在link标记中创建 rel 值。默认值:alternate

  • protocol

    将替换 baseURL 中此输出格式的"http://“或"https://"。

  • isPlainText

    使用 Go 的纯文本模板解析器进行模板解析。默认值: false

  • isHTML

    仅在与 HTML 类型格式相关的情况下使用,例如页面别名。默认值: false

  • noUgly

    用于关闭丑陋的 URL(如果在站点中设置了 uglyURLstrue)。默认值: false

  • notAlternative

    enable if it doesn’t make sense to include this format in an AlternativeOutputFormats format listing on Page (e.g., with CSS). Note that we use the term alternative and not alternate here, as it does not necessarily replace the other format. Default: false.

    如果在PageAlternativeOutputFormats 格式列表中包含此格式不合理(例如使用 CSS),则启用此选项。请注意,此处我们使用 alternative 而不是 alternate 一词,因为它并不一定替代其他格式。默认值: false

  • permalinkable

    使 .Permalink.RelPermalink 返回渲染输出格式而不是主格式(见下文)。默认情况下,对于 HTMLAMP 启用此选项。默认值: false

  • weight

    将其设置为非零值将用作第一个排序标准。

页面的输出格式

​ 在 Hugo 中,Page可以在文件系统上呈现为多种输出格式。

默认输出格式

​ 每个Page都有一个 Kind 属性,其默认输出格式是基于此属性设置的。

KindDefault Output Formats
pageHTML
homeHTML, RSS
sectionHTML, RSS
taxonomyHTML, RSS
termHTML, RSS

自定义输出格式

​ 这可以通过在Page前置元数据或站点配置(对所有站点或每种语言)中定义一个outputs列表来更改。

​ 站点配置文件中的示例:

config.

=== “yaml”

``` yaml
outputs:
  home:
  - HTML
  - AMP
  - RSS
  page:
  - HTML
```

=== “toml”

``` toml
[outputs]
  home = ['HTML', 'AMP', 'RSS']
  page = ['HTML']
```

=== “json”

``` json
{
   "outputs": {
      "home": [
         "HTML",
         "AMP",
         "RSS"
      ],
      "page": [
         "HTML"
      ]
   }
}
```

请注意,在上面的示例中,sectiontaxonomyterm的输出格式将保持其默认值 ["HTML", "RSS"]

KindDescriptionExample
homeThe landing page for the home page/index.html
pageThe landing page for a given pagemy-post page (/posts/my-post/index.html)
sectionThe landing page of a given sectionposts section (/posts/index.html)
taxonomyThe landing page for a taxonomytags taxonomy (/tags/index.html)
termThe landing page for one taxonomy’s termterm awesome in tags taxonomy (/tags/awesome/index.html)
  • outputs 定义是每种PageKindpagehomesectiontaxonomyterm)的。

  • These can be overridden per Page in the front matter of content files.

  • 所使用的名称(例如 HTMLAMP)必须与已定义的输出格式的Name匹配。

    这些名称不区分大小写。

  • 这些可以在内容文件的前置元数据中针对每个Page进行覆盖。

​ 以下是一个在内容文件中定义呈现Page输出格式的前置元数据的示例:

content/example.md

=== “yaml”

``` yaml
---
outputs:
- html
- amp
- json
title: Example
---
```

=== “toml”

``` toml
+++
outputs = ['html', 'amp', 'json']
title = 'Example'
+++
```

=== “json”

``` json
{
   "outputs": [
      "html",
      "amp",
      "json"
   ],
   "title": "Example"
}
```

输出格式列表

​ 每个Page都有 .OutputFormats(所有格式,包括当前格式)和 .AlternativeOutputFormats 变量,后者可用于在站点的 <head> 中创建link rel 列表。

1
2
3
{{ range .AlternativeOutputFormats -}}
<link rel="{{ .Rel }}" type="{{ .MediaType.Type }}" href="{{ .Permalink | safeURL }}">
{{ end -}}

输出格式链接

Page 上的 .Permalink.RelPermalink 将返回为该页面定义的第一个输出格式(通常为 HTML,如果没有其他定义的话)。这与调用它们的模板文件无关。

来自 single.json.json

1
2
3
4
{{ .RelPermalink }} > /that-page/
{{ with  .OutputFormats.Get "json" -}}
{{ .RelPermalink }} > /that-page/index.json
{{- end }}

​ 为了使它们返回当前模板文件的输出格式,给定的输出格式应设置其 permalinkable 属性为 true

与上面相同的模板文件,带有json 输出格式的 permalinkable 设置为 true:

1
2
3
4
{{ .RelPermalink }} > /that-page/index.json
{{ with  .OutputFormats.Get "html" -}}
{{ .RelPermalink }} > /that-page/
{{- end }}

​ 从内容文件中,您可以使用 refrelref 简码

1
2
[Neat](\{\{\< ref "blog/neat.md" "amp" \>\}\})
[Who](\{\{\< relref "about.md#who" "amp" \>\}\})

您的输出格式模板

​ 一个新的输出格式需要一个相应的模板才能渲染任何有用的内容。

​ 对于 Hugo 0.20 及更高版本的关键区别在于,Hugo 查看输出格式的 Name 和 MediaType 的 Suffixes,以选择用于渲染给定 Page 的模板。

​ 以下表格显示了不同输出格式的示例、所使用的后缀以及 Hugo 的模板查找顺序。表格中的所有示例都可以:

ExampleOutputFormatSuffixTemplate Lookup Order
“posts” 章节的单个页面HTMLhtml[layouts/posts/single.html.html layouts/posts/single.html layouts/_default/single.html.html layouts/_default/single.html]
“posts” 章节的单个页面基础模板HTMLhtml[layouts/posts/single-baseof.html.html layouts/posts/baseof.html.html layouts/posts/single-baseof.html layouts/posts/baseof.html layouts/_default/single-baseof.html.html layouts/_default/baseof.html.html layouts/_default/single-baseof.html layouts/_default/baseof.html]
Single page in “posts” section with layout setHTMLhtml[layouts/posts/demolayout.html.html layouts/posts/single.html.html layouts/posts/demolayout.html layouts/posts/single.html layouts/_default/demolayout.html.html layouts/_default/single.html.html layouts/_default/demolayout.html layouts/_default/single.html]
Base template for single page in “posts” section with layout setHTMLhtml[layouts/posts/demolayout-baseof.html.html layouts/posts/single-baseof.html.html layouts/posts/baseof.html.html layouts/posts/demolayout-baseof.html layouts/posts/single-baseof.html layouts/posts/baseof.html layouts/_default/demolayout-baseof.html.html layouts/_default/single-baseof.html.html layouts/_default/baseof.html.html layouts/_default/demolayout-baseof.html layouts/_default/single-baseof.html layouts/_default/baseof.html]
AMP single pageAMPhtml[layouts/posts/single.amp.html layouts/posts/single.html layouts/_default/single.amp.html layouts/_default/single.html]
AMP single page, French languageAMPhtml[layouts/posts/single.fr.amp.html layouts/posts/single.amp.html layouts/posts/single.fr.html layouts/posts/single.html layouts/_default/single.fr.amp.html layouts/_default/single.amp.html layouts/_default/single.fr.html layouts/_default/single.html]
Home pageHTMLhtml[layouts/index.html.html layouts/home.html.html layouts/list.html.html layouts/index.html layouts/home.html layouts/list.html layouts/_default/index.html.html layouts/_default/home.html.html layouts/_default/list.html.html layouts/_default/index.html layouts/_default/home.html layouts/_default/list.html]
Base template for home pageHTMLhtml[layouts/index-baseof.html.html layouts/home-baseof.html.html layouts/list-baseof.html.html layouts/baseof.html.html layouts/index-baseof.html layouts/home-baseof.html layouts/list-baseof.html layouts/baseof.html layouts/_default/index-baseof.html.html layouts/_default/home-baseof.html.html layouts/_default/list-baseof.html.html layouts/_default/baseof.html.html layouts/_default/index-baseof.html layouts/_default/home-baseof.html layouts/_default/list-baseof.html layouts/_default/baseof.html]
Home page with type setHTMLhtml[layouts/demotype/index.html.html layouts/demotype/home.html.html layouts/demotype/list.html.html layouts/demotype/index.html layouts/demotype/home.html layouts/demotype/list.html layouts/index.html.html layouts/home.html.html layouts/list.html.html layouts/index.html layouts/home.html layouts/list.html layouts/_default/index.html.html layouts/_default/home.html.html layouts/_default/list.html.html layouts/_default/index.html layouts/_default/home.html layouts/_default/list.html]
Base template for home page with type setHTMLhtml[layouts/demotype/index-baseof.html.html layouts/demotype/home-baseof.html.html layouts/demotype/list-baseof.html.html layouts/demotype/baseof.html.html layouts/demotype/index-baseof.html layouts/demotype/home-baseof.html layouts/demotype/list-baseof.html layouts/demotype/baseof.html layouts/index-baseof.html.html layouts/home-baseof.html.html layouts/list-baseof.html.html layouts/baseof.html.html layouts/index-baseof.html layouts/home-baseof.html layouts/list-baseof.html layouts/baseof.html layouts/_default/index-baseof.html.html layouts/_default/home-baseof.html.html layouts/_default/list-baseof.html.html layouts/_default/baseof.html.html layouts/_default/index-baseof.html layouts/_default/home-baseof.html layouts/_default/list-baseof.html layouts/_default/baseof.html]
Home page with layout setHTMLhtml[layouts/demolayout.html.html layouts/index.html.html layouts/home.html.html layouts/list.html.html layouts/demolayout.html layouts/index.html layouts/home.html layouts/list.html layouts/_default/demolayout.html.html layouts/_default/index.html.html layouts/_default/home.html.html layouts/_default/list.html.html layouts/_default/demolayout.html layouts/_default/index.html layouts/_default/home.html layouts/_default/list.html]
AMP home, French languageAMPhtml[layouts/index.fr.amp.html layouts/home.fr.amp.html layouts/list.fr.amp.html layouts/index.amp.html layouts/home.amp.html layouts/list.amp.html layouts/index.fr.html layouts/home.fr.html layouts/list.fr.html layouts/index.html layouts/home.html layouts/list.html layouts/_default/index.fr.amp.html layouts/_default/home.fr.amp.html layouts/_default/list.fr.amp.html layouts/_default/index.amp.html layouts/_default/home.amp.html layouts/_default/list.amp.html layouts/_default/index.fr.html layouts/_default/home.fr.html layouts/_default/list.fr.html layouts/_default/index.html layouts/_default/home.html layouts/_default/list.html]
JSON homeJSONjson[layouts/index.json.json layouts/home.json.json layouts/list.json.json layouts/index.json layouts/home.json layouts/list.json layouts/_default/index.json.json layouts/_default/home.json.json layouts/_default/list.json.json layouts/_default/index.json layouts/_default/home.json layouts/_default/list.json]
RSS homeRSSxml[layouts/index.rss.xml layouts/home.rss.xml layouts/rss.xml layouts/list.rss.xml layouts/index.xml layouts/home.xml layouts/list.xml layouts/_default/index.rss.xml layouts/_default/home.rss.xml layouts/_default/rss.xml layouts/_default/list.rss.xml layouts/_default/index.xml layouts/_default/home.xml layouts/_default/list.xml layouts/_internal/_default/rss.xml]
RSS section postsRSSxml[layouts/posts/section.rss.xml layouts/posts/rss.xml layouts/posts/list.rss.xml layouts/posts/section.xml layouts/posts/list.xml layouts/section/section.rss.xml layouts/section/rss.xml layouts/section/list.rss.xml layouts/section/section.xml layouts/section/list.xml layouts/_default/section.rss.xml layouts/_default/rss.xml layouts/_default/list.rss.xml layouts/_default/section.xml layouts/_default/list.xml layouts/_internal/_default/rss.xml]
Taxonomy in categoriesRSSxml[layouts/categories/category.terms.rss.xml layouts/categories/terms.rss.xml layouts/categories/taxonomy.rss.xml layouts/categories/rss.xml layouts/categories/list.rss.xml layouts/categories/category.terms.xml layouts/categories/terms.xml layouts/categories/taxonomy.xml layouts/categories/list.xml layouts/category/category.terms.rss.xml layouts/category/terms.rss.xml layouts/category/taxonomy.rss.xml layouts/category/rss.xml layouts/category/list.rss.xml layouts/category/category.terms.xml layouts/category/terms.xml layouts/category/taxonomy.xml layouts/category/list.xml layouts/taxonomy/category.terms.rss.xml layouts/taxonomy/terms.rss.xml layouts/taxonomy/taxonomy.rss.xml layouts/taxonomy/rss.xml layouts/taxonomy/list.rss.xml layouts/taxonomy/category.terms.xml layouts/taxonomy/terms.xml layouts/taxonomy/taxonomy.xml layouts/taxonomy/list.xml layouts/_default/category.terms.rss.xml layouts/_default/terms.rss.xml layouts/_default/taxonomy.rss.xml layouts/_default/rss.xml layouts/_default/list.rss.xml layouts/_default/category.terms.xml layouts/_default/terms.xml layouts/_default/taxonomy.xml layouts/_default/list.xml layouts/_internal/_default/rss.xml]
Term in categoriesRSSxml[layouts/categories/term.rss.xml layouts/categories/category.rss.xml layouts/categories/taxonomy.rss.xml layouts/categories/rss.xml layouts/categories/list.rss.xml layouts/categories/term.xml layouts/categories/category.xml layouts/categories/taxonomy.xml layouts/categories/list.xml layouts/term/term.rss.xml layouts/term/category.rss.xml layouts/term/taxonomy.rss.xml layouts/term/rss.xml layouts/term/list.rss.xml layouts/term/term.xml layouts/term/category.xml layouts/term/taxonomy.xml layouts/term/list.xml layouts/taxonomy/term.rss.xml layouts/taxonomy/category.rss.xml layouts/taxonomy/taxonomy.rss.xml layouts/taxonomy/rss.xml layouts/taxonomy/list.rss.xml layouts/taxonomy/term.xml layouts/taxonomy/category.xml layouts/taxonomy/taxonomy.xml layouts/taxonomy/list.xml layouts/category/term.rss.xml layouts/category/category.rss.xml layouts/category/taxonomy.rss.xml layouts/category/rss.xml layouts/category/list.rss.xml layouts/category/term.xml layouts/category/category.xml layouts/category/taxonomy.xml layouts/category/list.xml layouts/_default/term.rss.xml layouts/_default/category.rss.xml layouts/_default/taxonomy.rss.xml layouts/_default/rss.xml layouts/_default/list.rss.xml layouts/_default/term.xml layouts/_default/category.xml layouts/_default/taxonomy.xml layouts/_default/list.xml layouts/_internal/_default/rss.xml]
Section list for “posts” sectionHTMLhtml[layouts/posts/posts.html.html layouts/posts/section.html.html layouts/posts/list.html.html layouts/posts/posts.html layouts/posts/section.html layouts/posts/list.html layouts/section/posts.html.html layouts/section/section.html.html layouts/section/list.html.html layouts/section/posts.html layouts/section/section.html layouts/section/list.html layouts/_default/posts.html.html layouts/_default/section.html.html layouts/_default/list.html.html layouts/_default/posts.html layouts/_default/section.html layouts/_default/list.html]
Section list for “posts” section with type set to “blog”HTMLhtml[layouts/blog/posts.html.html layouts/blog/section.html.html layouts/blog/list.html.html layouts/blog/posts.html layouts/blog/section.html layouts/blog/list.html layouts/posts/posts.html.html layouts/posts/section.html.html layouts/posts/list.html.html layouts/posts/posts.html layouts/posts/section.html layouts/posts/list.html layouts/section/posts.html.html layouts/section/section.html.html layouts/section/list.html.html layouts/section/posts.html layouts/section/section.html layouts/section/list.html layouts/_default/posts.html.html layouts/_default/section.html.html layouts/_default/list.html.html layouts/_default/posts.html layouts/_default/section.html layouts/_default/list.html]
Section list for “posts” section with layout set to “demoLayout”HTMLhtml[layouts/posts/demolayout.html.html layouts/posts/posts.html.html layouts/posts/section.html.html layouts/posts/list.html.html layouts/posts/demolayout.html layouts/posts/posts.html layouts/posts/section.html layouts/posts/list.html layouts/section/demolayout.html.html layouts/section/posts.html.html layouts/section/section.html.html layouts/section/list.html.html layouts/section/demolayout.html layouts/section/posts.html layouts/section/section.html layouts/section/list.html layouts/_default/demolayout.html.html layouts/_default/posts.html.html layouts/_default/section.html.html layouts/_default/list.html.html layouts/_default/demolayout.html layouts/_default/posts.html layouts/_default/section.html layouts/_default/list.html]
Taxonomy list in categoriesHTMLhtml[layouts/categories/category.terms.html.html layouts/categories/terms.html.html layouts/categories/taxonomy.html.html layouts/categories/list.html.html layouts/categories/category.terms.html layouts/categories/terms.html layouts/categories/taxonomy.html layouts/categories/list.html layouts/category/category.terms.html.html layouts/category/terms.html.html layouts/category/taxonomy.html.html layouts/category/list.html.html layouts/category/category.terms.html layouts/category/terms.html layouts/category/taxonomy.html layouts/category/list.html layouts/taxonomy/category.terms.html.html layouts/taxonomy/terms.html.html layouts/taxonomy/taxonomy.html.html layouts/taxonomy/list.html.html layouts/taxonomy/category.terms.html layouts/taxonomy/terms.html layouts/taxonomy/taxonomy.html layouts/taxonomy/list.html layouts/_default/category.terms.html.html layouts/_default/terms.html.html layouts/_default/taxonomy.html.html layouts/_default/list.html.html layouts/_default/category.terms.html layouts/_default/terms.html layouts/_default/taxonomy.html layouts/_default/list.html]
Taxonomy term in categoriesHTMLhtml[layouts/categories/term.html.html layouts/categories/category.html.html layouts/categories/taxonomy.html.html layouts/categories/list.html.html layouts/categories/term.html layouts/categories/category.html layouts/categories/taxonomy.html layouts/categories/list.html layouts/term/term.html.html layouts/term/category.html.html layouts/term/taxonomy.html.html layouts/term/list.html.html layouts/term/term.html layouts/term/category.html layouts/term/taxonomy.html layouts/term/list.html layouts/taxonomy/term.html.html layouts/taxonomy/category.html.html layouts/taxonomy/taxonomy.html.html layouts/taxonomy/list.html.html layouts/taxonomy/term.html layouts/taxonomy/category.html layouts/taxonomy/taxonomy.html layouts/taxonomy/list.html layouts/category/term.html.html layouts/category/category.html.html layouts/category/taxonomy.html.html layouts/category/list.html.html layouts/category/term.html layouts/category/category.html layouts/category/taxonomy.html layouts/category/list.html layouts/_default/term.html.html layouts/_default/category.html.html layouts/_default/taxonomy.html.html layouts/_default/list.html.html layouts/_default/term.html layouts/_default/category.html layouts/_default/taxonomy.html layouts/_default/list.html]

​ Hugo 现在还可以检测 partials 的媒体类型和输出格式(如果可能的话),并使用这些信息来决定是否将 partial 解析为纯文本模板。

​ Hugo将查找给定的名称,因此您可以根据需要随意命名它。但是,如果要将其视为纯文本,请使用文件后缀,如果需要,还要使用输出格式的名称。 模式如下:

1
[partial name].[OutputFormat].[suffix]

​ 以下 partial 是一个纯文本模板(输出格式为 CSV,由于这是唯一带有后缀 csv 的输出格式,因此我们不需要包含输出格式的Name):

1
{{ partial "mytextpartial.csv" . }}

另请参阅

4 - 基础模板和块

Base Templates and Blocks - 基础模板和块

https://gohugo.io/templates/base/

​ 基本模板和块结构允许您定义主模板的外壳(即页面的Chrome)。

block关键字允许您定义页面一个或多个主模板的外壳,然后根据需要填充或覆盖部分(portions )。

基础模板查找顺序

​ 基础模板查找顺序紧跟它所应用的模板的查找顺序(例如,_default/list.html)。

​ 有关详细信息和示例,请参见模板查找顺序

定义基础模板

​ 以下定义了一个简单的基础模板 _default/baseof.html。作为默认模板,它是从所有页面渲染的外壳,除非您在查找顺序的开头指定另一个*baseof.html

layouts/_default/baseof.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>{{ block "title" . }}
      <!-- Blocks may include default content. -->
      {{ .Site.Title }}
    {{ end }}</title>
  </head>
  <body>
    <!-- Code that all your templates share, like a header -->
    {{ block "main" . }}
      <!-- The part of the page that begins to differ between templates -->
    {{ end }}
    {{ block "footer" . }}
    <!-- More shared code, perhaps a footer but that can be overridden if need be in  -->
    {{ end }}
  </body>
</html>

覆盖基础模板

​ 从上面的基本模板,您可以定义默认的列表模板。默认的列表模板将继承上面定义的所有代码,然后可以根据需要实现自己的"main"块:

layouts/_default/list.html

1
2
3
4
5
6
7
8
9
{{ define "main" }}
  <h1>Posts</h1>
  {{ range .Pages }}
    <article>
      <h2>{{ .Title }}</h2>
      {{ .Content }}
    </article>
  {{ end }}
{{ end }}

​ 这将用实际有用的内容替换我们(基本上为空的)"main"块的内容,对于列表中的内容,我们没有定义"title"块,因此在列表中保留了来自基础模板的内容。

​ 放置在块定义之外的代码会破坏您的布局,这甚至包括HTML注释。例如:

1
2
3
4
<!-- Seemingly harmless HTML comment..that will break your layout at build -->
{{ define "main" }}
...your code here
{{ end }}

请参见Hugo讨论论坛中的此主题

​ 以下显示了如何使用特定于默认单个页面模板的代码覆盖基础模板的"main""title"块区域:

layouts/_default/single.html

1
2
3
4
5
6
7
8
{{ define "title" }}
  <!-- This will override the default value set in baseof.html; i.e., "{{ .Site.Title }}" in the original example-->
  {{ .Title }} &ndash; {{ .Site.Title }}
{{ end }}
{{ define "main" }}
  <h1>{{ .Title }}</h1>
  {{ .Content }}
{{ end }}

另请参阅

5 - Markdown渲染钩子

Markdown Render Hooks - Markdown渲染钩子

https://gohugo.io/templates/render-hooks/

​ 渲染钩子允许自定义模板覆盖markdown渲染功能。

​ 请注意,这只支持Goldmark渲染器。

​ 您可以通过在layouts/_default/_markup中创建名称为render-{kind}的模板来覆盖默认的Markdown渲染为HTML的某些部分。

​ 您还可以在layouts/[type/section]/_markup中创建特定于type/section的钩子,例如:layouts/blog/_markup

​ 目前支持的钩子种类有:

​ 如果需要,您可以定义特定于输出格式语言的模板。您的layouts文件夹可能如下所示:

1
2
3
4
5
6
7
8
9
layouts/
└── _default/
    └── _markup/
        ├── render-codeblock-bash.html
        ├── render-codeblock.html
        ├── render-heading.html
        ├── render-image.html
        ├── render-image.rss.xml
        └── render-link.html

​ 以下是上述用法的一些示例:

  • 使用.GetPage解析链接引用。这将使链接可移植,因为您可以将./my-post.md(和在GitHub上可以使用的类似构造)转换为/blog/2019/01/01/my-post/等。
  • 为外部链接添加target=_blank
  • 解析和处理图像。
  • 添加标题链接

渲染钩子应用于标题、链接和图像

传递给render-linkrender-image的上下文

render-linkrender-image模板将接收到以下上下文:

  • Page

    正在被渲染的Page

  • Destination

    The URL.

  • Title

    title属性。

  • Text

    渲染后的(HTML)链接文本。

  • PlainText

    上述文本的纯文本版本。

传递给render-heading的上下文

render-heading模板将接收以下上下文:

  • Page

    正在被渲染的页面

  • Level

    标题级别(1-6)

  • Anchor

    在该页面中唯一的自动生成的HTML id。

  • Text

    被渲染后的(HTML)文本。

  • PlainText

    上述内容的纯文本版本。

  • Attributes (map)

    一个属性映射(例如idclass)。需要注意的是,对于链接,这个映射目前始终为空。

The render-image templates will also receive:

render-image模板还将接收:

带标题的Markdown链接示例

1
[Text](https://www.gohugo.io "Title")

​ 以下是render-link.html模板的代码示例:

layouts/_default/_markup/render-link.html

1
<a href="{{ .Destination | safeURL }}"{{ with .Title }} title="{{ . }}"{{ end }}{{ if strings.HasPrefix .Destination "http" }} target="_blank" rel="noopener"{{ end }}>{{ .Text | safeHTML }}</a>

图像Markdown示例

1
![Text](https://gohugo.io/images/hugo-logo-wide.svg "Title")

​ 以下是render-image.html模板的代码示例:

layouts/_default/_markup/render-image.html

1
2
3
<p class="md__image">
  <img src="{{ .Destination | safeURL }}" alt="{{ .Text }}" {{ with .Title }} title="{{ . }}"{{ end }} />
</p>

标题链接示例

​ 给定此模板文件

layouts/_default/_markup/render-heading.html

1
<h{{ .Level }} id="{{ .Anchor | safeURL }}">{{ .Text | safeHTML }} <a href="#{{ .Anchor | safeURL }}">¶</a></h{{ .Level }}>

​ 以及这个 markdown

1
### Section A

​ 渲染出的 HTML 代码将是

1
<h3 id="section-a">Section A <a href="#section-a">¶</a></h3>

代码块的渲染钩子

New in v0.93.0

​ 您可以为所有代码块或特定类型/语言(例如下面的bash)添加钩子模板:

image-20230425200136050

​ 这些代码块的默认行为是进行代码高亮,但是由于可以向这些代码块传递属性,因此它们可以用于几乎任何事情。一个示例是内置的GoAT Diagrams或这个Mermaid Diagram Code Block Hook示例。

​ 代码块模板中您可以获取到的上下文(".")包括:

  • Type (string)

    代码块的类型。这将是编程语言,例如bash,用于进行代码高亮。

  • Attributes (map)

    从Markdown传递的属性(例如{ attrName1=attrValue1 attrName2="attr Value 2" })。

  • Options (map)

    Chroma 高亮处理选项。仅在 Type 是已知的 Chroma Lexer 时才填充。

  • Inner (string)

    代码围栏之间的文本。

  • Ordinal (integer)

    当前文档中所有代码块的从零开始的序数。

  • Page

    所属的Page

  • Position

    在错误日志中有用,因为它会打印出文件名和位置(行号、列号),例如 {{ errorf "error in code block: %s" .Position }}

另请参阅

6 - 在Hugo中的内容列表

Lists of Content in Hugo - 在Hugo中的内容列表

https://gohugo.io/templates/lists/

​ 列表在 Hugo 中在渲染站点主页、章节页面、分类列表或分类术语列表时具有特定的含义和用法。

什么是列表页面模板?

​ 列表页面模板是用于在单个 HTML 页面中渲染多个内容的模板。唯一的例外是主页,它仍然是一个列表,但有自己的专用模板

​ Hugo 在其最真实的意义上使用列表这个术语;即按字母或数字顺序的一系列材料。Hugo 在任何传统上列出内容的输出 HTML 页面上都使用列表模板:

​ 有关模板查找顺序,请参见模板查找

​ 列表页面的概念源于Web的分层心理模型,最好通过可视化进行演示:

Image demonstrating a hierarchical website sitemap.

列表默认值

默认模板

​ 由于章节列表和分类列表(注意,不是分类术语列表)在模板方面都是列表,因此它们在查找顺序中都有相同的终止默认值 _default/list.htmlthemes/<THEME>/layouts/_default/list.html。此外,章节列表分类列表_default 中都有自己的默认列表模板。

​ 有关完整参考,请参阅模板查找顺序

将内容和前置元数据添加到列表页

​ 自从 v0.18 以来,Hugo 中的所有内容都是Page。这意味着列表页面和主页可以有关联的内容文件(即_index.md),其中包含页面元数据(即前置元数据)和内容。

​ 这种新模型允许您通过 .Params 包含特定于列表的前置元数据,并且意味着列表模板(例如 layouts/_default/list.html)可以访问所有页面变量

​ 重要的是要注意,所有 _index.md 内容文件都将根据列表模板而不是单个页面模板进行渲染。

示例项目目录

​ 以下是一个典型的Hugo项目的content目录的示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
.
...
├── content
|   ├── posts
|   |   ├── _index.md
|   |   ├── post-01.md
|   |   └── post-02.md
|   └── quote
|   |   ├── quote-01.md
|   |   └── quote-02.md
...

​ 使用上述示例,假设您在content/posts/_index.md中有以下内容:

content/posts/_index.md

1
2
3
4
5
6
7
8
9
---
title: My Go Journey
date: 2017-03-23
publishdate: 2017-03-24
---

I decided to start learning Go in March 2017.

Follow my journey through this new blog.

​ 现在,您可以在列表模板中访问此_index.md的内容:

layouts/_default/list.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
{{ define "main" }}
<main>
  <article>
    <header>
      <h1>{{ .Title} }</h1>
    </header>
    <!-- "{{ .Content} }" pulls from the markdown content of the corresponding _index.md -->
    {{ .Content }}
  </article>
  <ul>
    <!-- Ranges through content/posts/*.md -->
    {{ range .Pages }}
      <li>
        <a href="{{ .Permalink }}">{{ .Date.Format "2006-01-02" }} | {{ .Title }}</a>
      </li>
    {{ end }}
  </ul>
</main>
{{ end }}

​ 上面将输出以下 HTML:

example.com/posts/index.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
<!--top of your baseof code-->
<main>
    <article>
        <header>
            <h1>My Go Journey</h1>
        </header>
        <p>I decided to start learning Go in March 2017.</p>
        <p>Follow my journey through this new blog.</p>
    </article>
    <ul>
        <li><a href="/posts/post-01/">Post 1</a></li>
        <li><a href="/posts/post-02/">Post 2</a></li>
    </ul>
</main>
<!--bottom of your baseof-->

没有 _index.md 的列表页面

​ 您不必为每个列表页面(即章节、分类、分类术语等)或主页创建 _index.md 文件。如果 Hugo 在渲染列表模板时在相应的内容章节中找不到 _index.md,则将创建该页面,但没有 {{ .Content }},只有 .Title 等的默认值。

​ 将相同的 layouts/_default/list.html 模板应用于上面的 quotes 章节将渲染以下输出。请注意,quotes 没有可供提取的 _index.md 文件:

example.com/quote/index.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<!--baseof-->
<main>
    <article>
        <header>
        <!-- Hugo assumes that .Title is the name of the section since there is no _index.md content file from which to pull a "title:" field -->
            <h1>Quotes</h1>
        </header>
    </article>
    <ul>
        <li><a href="https://example.com/quote/quotes-01/">Quote 1</a></li>
        <li><a href="https://example.com/quote/quotes-02/">Quote 2</a></li>
    </ul>
</main>
<!--baseof-->

默认情况下,Hugo会将列表标题进行复数化处理,因此在使用 .Title 页面变量时,quote部分会变为"Quotes"。您可以通过在站点配置中使用pluralizeListTitles指令来更改此设置。

示例列表模板

章节模板

​ 这个列表模板是从spf13.com的模板进行了略微修改。它使用局部模板来渲染页面的外壳,而不是使用基础模板。下面的示例还使用了内容视图模板 li.htmlsummary.html

layouts/section/posts.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
{{ partial "header.html" . }}
{{ partial "subheader.html" . }}
<main>
  <div>
   <h1>{{ .Title }}</h1>
        <ul>
        <!-- Renders the li.html content view for each content/posts/*.md -->
            {{ range .Pages }}
                {{ .Render "li" }}
            {{ end }}
        </ul>
  </div>
</main>
{{ partial "footer.html" . }}

分类模板

layouts/_default/taxonomy.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
{{ define "main" }}
<main>
  <div>
   <h1>{{ .Title }}</h1>
   <!-- ranges through each of the content files associated with a particular taxonomy term and renders the summary.html content view -->
    {{ range .Pages }}
        {{ .Render "summary" }}
    {{ end }}
  </div>
</main>
{{ end }}

内容排序

​ Hugo 根据您在前置元数据中提供的内容来渲染列表。除了合理的默认值外,Hugo 还提供了多种方法,以便在列表模板内快速进行内容排序:

Default: Weight > Date > LinkTitle > FilePath

layouts/partials/default-order.html

1
2
3
4
5
6
7
8
<ul>
    {{ range .Pages }}
        <li>
            <h1><a href="{{ .Permalink }}">{{ .Title }}</a></h1>
            <time>{{ .Date.Format "Mon, Jan 2, 2006" }}</time>
        </li>
    {{ end }}
</ul>

按权重

​ 较低的权重具有较高的优先级。因此,权重较低的内容将排在前面。

layouts/partials/by-weight.html

1
2
3
4
5
6
7
8
<ul>
    {{ range .Pages.ByWeight }}
        <li>
            <h1><a href="{{ .Permalink }}">{{ .Title }}</a></h1>
            <time>{{ .Date.Format "Mon, Jan 2, 2006" }}</time>
        </li>
    {{ end }}
</ul>

按日期

layouts/partials/by-date.html

1
2
3
4
5
6
7
8
9
<ul>
    <!-- orders content according to the "date" field in front matter -->
    {{ range .Pages.ByDate }}
        <li>
            <h1><a href="{{ .Permalink }}">{{ .Title }}</a></h1>
            <time>{{ .Date.Format "Mon, Jan 2, 2006" }}</time>
        </li>
    {{ end }}
</ul>

按发布日期

layouts/partials/by-publish-date.html

1
2
3
4
5
6
7
8
9
<ul>
    <!-- orders content according to the "publishdate" field in front matter -->
    {{ range .Pages.ByPublishDate }}
        <li>
            <h1><a href="{{ .Permalink }}">{{ .Title }}</a></h1>
            <time>{{ .Date.Format "Mon, Jan 2, 2006" }}</time>
        </li>
    {{ end }}
</ul>

按到期日期

layouts/partials/by-expiry-date.html

1
2
3
4
5
6
7
8
<ul>
    {{ range .Pages.ByExpiryDate }}
        <li>
            <h1><a href="{{ .Permalink }}">{{ .Title }}</a></h1>
            <time>{{ .Date.Format "Mon, Jan 2, 2006" }}</time>
        </li>
    {{ end }}
</ul>

按最后修改日期

layouts/partials/by-last-mod.html

1
2
3
4
5
6
7
8
9
<ul>
    <!-- orders content according to the "lastmod" field in front matter -->
    {{ range .Pages.ByLastmod }}
        <li>
            <h1><a href="{{ .Permalink }}">{{ .Title }}</a></h1>
            <time>{{ .Date.Format "Mon, Jan 2, 2006" }}</time>
        </li>
    {{ end }}
</ul>

按长度

layouts/partials/by-length.html

1
2
3
4
5
6
7
8
9
<ul>
    <!-- orders content according to content length in ascending order (i.e., the shortest content will be listed first) -->
    {{ range .Pages.ByLength }}
        <li>
            <h1><a href="{{ .Permalink }}">{{ .Title }}</a></h1>
            <time>{{ .Date.Format "Mon, Jan 2, 2006" }}</time>
        </li>
    {{ end }}
</ul>

按标题

layouts/partials/by-title.html

1
2
3
4
5
6
7
8
9
<ul>
    <!-- ranges through content in ascending order according to the "title" field set in front matter -->
    {{ range .Pages.ByTitle }}
        <li>
            <h1><a href="{{ .Permalink }}">{{ .Title }}</a></h1>
            <time>{{ .Date.Format "Mon, Jan 2, 2006" }}</time>
        </li>
    {{ end }}
</ul>

按链接标题

layouts/partials/by-link-title.html

1
2
3
4
5
6
7
8
9
<ul>
    <!-- ranges through content in ascending order according to the "linktitle" field in front matter. If a "linktitle" field is not set, the range will start with content that only has a "title" field and use that value for .LinkTitle -->
    {{ range .Pages.ByLinkTitle }}
        <li>
            <h1><a href="{{ .Permalink }}">{{ .LinkTitle }}</a></h1>
            <time>{{ .Date.Format "Mon, Jan 2, 2006" }}</time>
        </li>
    {{ end }}
</ul>

按参数

​ 根据指定的前置元数据参数进行排序。如果内容没有指定的前置元数据字段,则使用站点的 .Site.Params 默认值。如果在某些条目中根本找不到该参数,这些条目将一起出现在排序的末尾。

layouts/partials/by-rating.html

1
2
3
4
<!-- Ranges through content according to the "rating" field set in front matter -->
{{ range (.Pages.ByParam "rating") }}
  <!-- ... -->
{{ end }}

​ 如果目标的前置元数据字段嵌套在另一个字段下,则您可以使用点表示法来访问该字段。

layouts/partials/by-nested-param.html

1
2
3
{{ range (.Pages.ByParam "author.last_name") }}
  <!-- ... -->
{{ end }}

倒序排序

​ 可以将倒序排序应用于上述任何一种方法。以下示例以 ByDate 为例:

layouts/partials/by-date-reverse.html

1
2
3
4
5
6
7
8
<ul>
    {{ range .Pages.ByDate.Reverse }}
        <li>
            <h1><a href="{{ .Permalink }}">{{ .Title }}</a></h1>
            <time>{{ .Date.Format "Mon, Jan 2, 2006" }}</time>
        </li>
    {{ end }}
</ul>

分组内容

​ Hugo 提供一些函数,可按章节、类型、日期等对页面进行分组。

按页面字段

layouts/partials/by-page-field.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<!-- Groups content according to content section. The ".Key" in this instance will be the section's title. -->
{{ range .Pages.GroupBy "Section" }}
<h3>{{ .Key }}</h3>
<ul>
    {{ range .Pages }}
    <li>
    <a href="{{ .Permalink }}">{{ .Title }}</a>
    <div class="meta">{{ .Date.Format "Mon, Jan 2, 2006" }}</div>
    </li>
    {{ end }}
</ul>
{{ end }}

​ 在上面的例子中,您可能希望 {{ .Title }} 指向您已添加到 _index.md 文件中的title字段。您可以使用 .GetPage 函数来访问此值:

layouts/partials/by-page-field.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
<!-- Groups content according to content section.-->
{{ range .Pages.GroupBy "Section" }}
<!-- Checks for existence of _index.md for a section; if available, pulls from "title" in front matter -->
{{ with $.Site.GetPage "section" .Key }}
<h3>{{ .Title }}</h3>
{{ else }}
<!-- If no _index.md is available, ".Key" defaults to the section title and filters to title casing -->
<h3>{{ .Key | title }}</h3>
{{ end }}
<ul>
    {{ range .Pages }}
    <li>
    <a href="{{ .Permalink }}">{{ .Title }}</a>
    <div class="meta">{{ .Date.Format "Mon, Jan 2, 2006" }}</div>
    </li>
    {{ end }}
</ul>
{{ end }}

按日期

layouts/partials/by-page-date.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<!-- Groups content by month according to the "date" field in front matter -->
{{ range .Pages.GroupByDate "2006-01" }}
<h3>{{ .Key }}</h3>
<ul>
    {{ range .Pages }}
    <li>
    <a href="{{ .Permalink }}">{{ .Title }}</a>
    <div class="meta">{{ .Date.Format "Mon, Jan 2, 2006" }}</div>
    </li>
    {{ end }}
</ul>
{{ end }}

在新版本 v0.97.0 中GroupByDate 函数接受与 time.Format 中相同的时间格式,并且结果中的 .Key 会根据当前语言进行本地化。

按发布日期

layouts/partials/by-page-publish-date.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<!-- Groups content by month according to the "publishDate" field in front matter -->
{{ range .Pages.GroupByPublishDate "2006-01" }}
<h3>{{ .Key }}</h3>
<ul>
    {{ range .Pages }}
    <li>
    <a href="{{ .Permalink }}">{{ .Title }}</a>
    <div class="meta">{{ .PublishDate.Format "Mon, Jan 2, 2006" }}</div>
    </li>
    {{ end }}
</ul>
{{ end }}

在新版本 v0.97.0 中GroupByDate 函数接受与 time.Format 中相同的时间格式,并且结果中的 .Key 会根据当前语言进行本地化。

按照上次修改时间

layouts/partials/by-page-lastmod.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<!-- Groups content by month according to the "lastMod" field in front matter -->
{{ range .Pages.GroupByLastmod "2006-01" }}
<h3>{{ .Key }}</h3>
<ul>
    {{ range .Pages }}
    <li>
    <a href="{{ .Permalink }}">{{ .Title }}</a>
    <div class="meta">{{ .Lastmod.Format "Mon, Jan 2, 2006" }}</div>
    </li>
    {{ end }}
</ul>
{{ end }}

在新版本 v0.97.0 中GroupByDate 函数接受与 time.Format 中相同的时间格式,并且结果中的 .Key 会根据当前语言进行本地化。

按过期日期

layouts/partials/by-page-expiry-date.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<!-- Groups content by month according to the "expiryDate" field in front matter -->
{{ range .Pages.GroupByExpiryDate "2006-01" }}
<h3>{{ .Key }}</h3>
<ul>
    {{ range .Pages }}
    <li>
    <a href="{{ .Permalink }}">{{ .Title }}</a>
    <div class="meta">{{ .ExpiryDate.Format "Mon, Jan 2, 2006" }}</div>
    </li>
    {{ end }}
</ul>
{{ end }}

在新版本 v0.97.0 中GroupByDate 函数接受与 time.Format 中相同的时间格式,并且结果中的 .Key 会根据当前语言进行本地化。

按照页面参数

layouts/partials/by-page-param.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<!-- Groups content according to the "param_key" field in front matter -->
{{ range .Pages.GroupByParam "param_key" }}
<h3>{{ .Key }}</h3>
<ul>
    {{ range .Pages }}
    <li>
    <a href="{{ .Permalink }}">{{ .Title }}</a>
    <div class="meta">{{ .Date.Format "Mon, Jan 2, 2006" }}</div>
    </li>
    {{ end }}
</ul>
{{ end }}

按照页面参数和日期格式

​ 在按date分组的模板中,使用了 Go 的布局字符串来进一步分组。有关如何使用 Go 的布局字符串格式化 Hugo 中的日期的更多示例,请参见 Format 函数

layouts/partials/by-page-param-as-date.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<!-- Groups content by month according to the "param_key" field in front matter -->
{{ range .Pages.GroupByParamDate "param_key" "2006-01" }}
<h3>{{ .Key }}</h3>
<ul>
    {{ range .Pages }}
    <li>
    <a href="{{ .Permalink }}">{{ .Title }}</a>
    <div class="meta">{{ .Date.Format "Mon, Jan 2, 2006" }}</div>
    </li>
    {{ end }}
</ul>
{{ end }}

反向键顺序

​ 分组的排序是按字母数字顺序的键进行排序(A-Z、1-100),并按照日期的逆序排列(即,最新的日期排在最前面)。

​ 虽然这些是逻辑上的默认值,但并不总是期望的排序。有两种不同的语法可以更改 Hugo 的默认分组排序,它们都以相同的方式工作。

1. 添加 Reverse 方法

1
2
{{ range (.Pages.GroupBy "Section").Reverse }}
{{ range (.Pages.GroupByDate "2006-01").Reverse }}

2. 提供另一种方向

1
2
{{ range .Pages.GroupByDate "2006-01" "asc" }}
{{ range .Pages.GroupBy "Section" "desc" }}

组内排序

​ 由于Grouping返回一个{{ .Key }}和一个页面片段,因此上述所有排序方法都可用。

​ 以下是示例的排序方式:

  1. 根据前置元数据中的date字段按月份分组内容。
  2. 按升序列出分组(即最旧的分组先列出)。
  3. 每个分组内的页面根据title按字母顺序排序。

layouts/partials/by-group-by-page.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
{{ range .Pages.GroupByDate "2006-01" "asc" }}
<h3>{{ .Key }}</h3>
<ul>
    {{ range .Pages.ByTitle }}
    <li>
    <a href="{{ .Permalink }}">{{ .Title }}</a>
    <div class="meta">{{ .Date.Format "Mon, Jan 2, 2006" }}</div>
    </li>
    {{ end }}
</ul>
{{ end }}

筛选和限制列表

​ 有时,您只想列出可用内容的子集。一个常见的方法是仅在博客主页上显示主要章节的文章。

​ 有关详细信息,请参见where函数first函数的文档。

另请参阅

7 - 主页模板

Homepage Template - 主页模板

https://gohugo.io/templates/homepage/

​ 站点的主页通常与其他页面格式不同。因此,Hugo 使您能够轻松地将新站点的主页定义为独特的模板。

​ 主页是一个Page,因此可使用所有页面变量站点变量

​ 主页模板是构建站点所必需的唯一模板,因此在启动新站点和模板时非常有用。如果您正在开发单页面站点,则它也是唯一必需的模板。

主页模板查找顺序

​ 请参见模板查找

向主页添加内容和前置元数据

​ 主页与 Hugo 中的其他列表页面类似,可以从 _index.md 文件接受内容和 前置元数据。该文件应该位于您的 content 文件夹的根目录下(即 content/_index.md)。然后,您可以像处理其他任何内容文件一样向主页添加正文和元数据(metadata )。

​ 有关如何使用 _index.md 向列表页面添加内容和前置元数据的更多信息,请参见下面的主页模板或内容组织

示例主页模板

​ 以下是一个主页模板示例,它使用 partial基础模板和位于 content/_index.md 中的内容文件来填充 {{ .Title }}{{ .Content }} 页面变量

layouts/index.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
{{ define "main" }}
  <main aria-role="main">
    <header class="homepage-header">
      <h1>{{ .Title }}</h1>
      {{ with .Params.subtitle }}
      <span class="subtitle">{{ . }}</span>
      {{ end }}
    </header>
    <div class="homepage-content">
      <!-- Note that the content for index.html, as a sort of list page, will pull from content/_index.md -->
      {{ .Content }}
    </div>
    <div>
      {{ range first 10 .Site.RegularPages }}
          {{ .Render "summary" }}
      {{ end }}
    </div>
  </main>
{{ end }}

8 - 分类法(Taxonomy)模板

Taxonomy Templates - 分类法(Taxonomy)模板

https://gohugo.io/templates/taxonomy-templates/

​ Taxonomy(分类法)模板包括分类法列表页面、分类法条目页面以及在单页模板中使用分类法。

​ Hugo 支持用户定义的内容分组,称为分类法。分类法是展示内容之间逻辑关系的分类法方法。如果您对 Hugo 如何利用这个强大功能不熟悉,请参见内容管理下的分类法

​ Hugo 提供了多种在项目模板中使用分类法的方式:

分类法列表模板

​ 分类法列表页面模板是列表,因此具有可用于列表页面的所有变量和方法。

分类法列表模板查找顺序

​ 请参见模板查找

分类法条目模板

分类法条目模板查找顺序

​ 请参见模板查找

分类法方法

A Taxonomy is a map[string]WeightedPages.

​ 分类法是一个 map[string]WeightedPages

  • .Get(term)

    返回条目的 WeightedPages。

  • .Count(term)

    分配给该条目的内容数量。

  • .Alphabetical

    Returns an OrderedTaxonomy (slice) ordered by Term.

    返回一个按条目排序的 OrderedTaxonomy(切片)。

  • .ByCount

    返回一个按条目数量排序的 OrderedTaxonomy(切片)。

  • .Reverse

    返回一个按相反顺序排序的 OrderedTaxonomy(切片)。必须与 一个OrderedTaxonomy 一起使用。

OrderedTaxonomy

​ 由于 Map 是无序的,因此 OrderedTaxonomy 是一个具有定义顺序的特殊结构。

1
2
3
4
[]struct {
    Name          string
    WeightedPages WeightedPages
}

​ 该切片的每个元素都有:

  • .Term

    使用的条目。

  • .WeightedPages

    一个加权页面的切片。

  • .Count

    分配给该条目的内容数量。

  • .Pages

    分配给该条目的所有页面。所有列表方法都可用于此。

WeightedPages

​ WeightedPages是WeightedPage的一个切片。

1
type WeightedPages []WeightedPage
  • .Count(term)

    被分配到此条目的内容数量。

  • .Pages

    返回一个页面的切片,可以使用任何 列表方法 进行排序。

在分类法条目模板中显示自定义元数据

​ 如果您需要为每个分类法条目显示自定义元数据,您需要在/content/<TAXONOMY>/<TERM>/_index.md路径下为该条目创建一个页面,并在其前置元数据中添加元数据,如分类法文档中所述。以其中显示的演员分类法为例,在您的分类法条目模板中,您可以通过迭代变量.Pages来访问您的自定义字段:

1
2
3
4
5
6
7
8
<ul>
    {{ range .Pages }}
        <li>
            <a href="{{ .Permalink }}">{{ .Title }}</a>
            {{ .Params.wikipedia }}
        </li>
    {{ end }}
</ul>

排序分类法

​ 分类法可以按字母键或分配给该键的内容数量排序。

按字母顺序示例

1
2
3
4
5
<ul>
    {{ range .Data.Terms.Alphabetical }}
            <li><a href="{{ .Page.Permalink }}">{{ .Page.Title }}</a> {{ .Count }}</li>
    {{ end }}
</ul>

在分类法中排序内容

​ Hugo在分类法中使用dateweight来排序内容。

​ 在 Hugo 中,每篇内容可以选择性地被分配一个日期。它也可以为每个所属分类法分配一个权重。

​ 当在分类法中迭代内容时,默认排序方式与用于章节和列表页面相同:首先按权重,然后按日期排序。这意味着,如果两篇内容的权重相同,则最近日期的内容将首先显示。

​ 任何一篇内容的默认权重为0。零意味着"does not have a weight",而不是"has a weight of numerical value zero"。

​ 因此,权重为零的条目被特殊处理:如果两个页面的权重不相等,并且其中一个是零,则具有零权重的页面将始终出现在另一个页面之后,而不用考虑另一个页面的权重。因此,应谨慎使用零权重:例如,如果正权重和负权重都用于在两个方向上扩展序列,则具有零权重的页面将不会出现在列表的中间,而是在末尾。

分配权重

​ 每个内容可以分别为它所属的每个分类法(taxonomies)赋予一个权重(weight)。

content/example.md

=== “yaml”

``` yaml
---
categories:
- d
categories_weight: 44
tags:
- a
- b
- c
tags_weight: 22
title: Example
---
```

=== “toml”

``` toml
+++
categories = ['d']
categories_weight = 44
tags = ['a', 'b', 'c']
tags_weight = 22
title = 'Example'
+++
```

=== “json”

``` json
{
   "categories": [
      "d"
   ],
   "categories_weight": 44,
   "tags": [
      "a",
      "b",
      "c"
   ],
   "tags_weight": 22,
   "title": "Example"
}
```

​ 惯例是使用taxonomyname_weight

​ 在上面的例子中,这篇内容在渲染分配给"tag" 分类法中的"a"、“b” 和 “c” 值的页面时具有22的权重。

​ 在渲染’d’类别时,它还被赋予了44的权重。

​ 这样做可以使同一篇内容在不同的分类法中出现在不同的位置。

​ 目前,分类法仅支持默认的内容排序方式,即权重->日期。

​ 使用分类法将需要提供两种不同的模板。

​ 这两中模板在模板章节中都有详细介绍。

列表模板是用于在单个HTML页面中渲染多篇内容的任一模板。此模板将用于生成所有自动创建的分类法页面。

分类法模板是用于生成给定模板的条目列表的模板。

​ 除了使用 Hugo 自动生成的 列表模板 来创建分类法页面外,还有四种常见的方式可以展示您的分类法数据:

  1. 对于给定篇的内容,您可以列出附加的条目
  2. 对于给定篇的内容,您可以列出具有相同条目的其他内容
  3. 您可以列出某一分类法的所有条目
  4. 您可以列出所有分类法(及其条目)

显示单篇内容的分类法

​ 在内容模板中,您可能希望显示分配给该内容的分类法。

​ 由于我们利用前置元数据系统为内容定义分类法,因此分配给每篇内容的分类法位于通常的位置(即 .Params.<TAXONOMYPLURAL>)。

示例:在单页模板中列出标签

1
2
3
4
5
<ul>
    {{ range (.GetTerms "tags") }}
        <li><a href="{{ .Permalink }}">{{ .LinkTitle }}</a></li>
    {{ end }}
</ul>

​ 如果您想要内联列出分类法,您将需要注意标题中的可选复数结尾(如果有多个分类法),以及逗号。假设我们有一个名为 “directors” 的分类法,如 TOML 格式的前置元数据所示:directors: [ "Joel Coen", "Ethan Coen" ]

​ 要列出这样的分类法,请使用以下方法:

示例:在单页模板中使用逗号分隔标签

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{{ $taxo := "directors" }} <!-- Use the plural form here -->
{{ with .Param $taxo }}
    <strong>Director{{ if gt (len .) 1 }}s{{ end }}:</strong>
    {{ range $index, $director := . }}
        {{- if gt $index 0 }}, {{ end -}}
        {{ with $.Site.GetPage (printf "/%s/%s" $taxo $director) -}}
            <a href="{{ .Permalink }}">{{ $director }}</a>
        {{- end -}}
    {{- end -}}
{{ end }}

​ 或者,如果只需要使用分隔符列出分类法,则可以使用delimit 模板函数作为快捷方式。详见GitHub上的#2143讨论。

列出具有相同分类法条目的内容

​ 如果您正在使用分类法来管理一系列文章,您可以列出与同一分类法相关联的各个页面。这也是一种快速粗略的方法来展示相关内容:

示例:显示同一系列的内容

1
2
3
4
5
<ul>
    {{ range .Site.Taxonomies.series.golang }}
        <li><a href="{{ .Page.RelPermalink }}">{{ .Page.Title }}</a></li>
    {{ end }}
</ul>

列出给定分类法中的所有内容

​ 这在侧边栏中作为"特色内容"将非常有用。您甚至可以通过为内容分配不同的条目来创建不同的"特色内容" 章节。

示例:对"Featured"内容进行分组

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<section id="menu">
    <ul>
        {{ range $key, $taxonomy := .Site.Taxonomies.featured }}
        <li>{{ $key }}</li>
        <ul>
            {{ range $taxonomy.Pages }}
            <li hugo-nav="{{ .RelPermalink }}"><a href="{{ .Permalink }}">{{ .LinkTitle }}</a></li>
            {{ end }}
        </ul>
        {{ end }}
    </ul>
</section>

渲染站点的分类法

​ 如果您希望显示站点分类法的所有键列表,可以从每个页面都可以访问的.Site变量中检索它们。

​ 这可以采用标签云、菜单或简单列表的形式。

​ 以下示例显示站点标签分类法中的所有条目:

示例:列出所有站点标签

1
2
3
4
5
<ul>
    {{ range .Site.Taxonomies.tags }}
            <li><a href="{{ .Page.Permalink }}">{{ .Page.Title }}</a> {{ .Count }}</li>
    {{ end }}
</ul>

示例:列出所有分类法、条目和分配的内容

​ 这个示例将列出所有分类法及其条目,以及分配给每个条目的所有内容。

layouts/partials/all-taxonomies.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
<section>
    <ul id="all-taxonomies">
        {{ range $taxonomy_term, $taxonomy := .Site.Taxonomies }}
            {{ with $.Site.GetPage (printf "/%s" $taxonomy_term) }}
                <li><a href="{{ .Permalink }}">{{ $taxonomy_term }}</a>
                    <ul>
                        {{ range $key, $value := $taxonomy }}
                            <li>{{ $key }}</li>
                            <ul>
                                {{ range $value.Pages }}
                                    <li hugo-nav="{{ .RelPermalink }}">
                                        <a href="{{ .Permalink }}">{{ .LinkTitle }}</a>
                                    </li>
                                {{ end }}
                            </ul>
                        {{ end }}
                    </ul>
                </li>
            {{ end }}
        {{ end }}
    </ul>
</section>

.Site.GetPage 用于分类法

​ 由于分类法是列表,可以使用.GetPage函数使用简洁的语法获取与特定分类法条目相关联的所有页面。下面对站点上所有标签进行全面的遍历,并链接到每个条目的单独分类页面,而不必使用上面的"列出所有站点标签"示例中更脆弱的URL构建方法:

links-to-all-tags.html

1
2
3
4
5
6
7
8
{{ $taxo := "tags" }}
<ul class="{{ $taxo }}">
    {{ with ($.Site.GetPage (printf "/%s" $taxo)) }}
        {{ range .Pages }}
            <li><a href="{{ .Permalink }}">{{ .Title }}</a></li>
        {{ end }}
    {{ end }}
</ul>

另请参阅

9 - 章节页面模板

Section Page Templates - 章节页面模板

https://gohugo.io/templates/section-templates/

​ 用于章节页面的模板是列表,因此具有所有可用于列举页面的变量和方法。

向章节模板添加内容和前置元数据

​ 为了有效利用章节页面模板,您应首先了解Hugo的内容组织方式,特别是添加内容和前置元数据到章节和其他列表页面的_index.md文件的目的。

章节模板查找顺序

​ 请参见模板查找

页面种类

​ Hugo 中的每个Page都有一个 .Kind 属性。

KindDescriptionExample
home主页的着陆页/index.html
page指定页面的着陆页my-post page (/posts/my-post/index.html)
section指定章节的着陆页posts section (/posts/index.html)
taxonomy分类的着陆页tags taxonomy (/tags/index.html)
term某一分类条目的着陆页term awesome in tags taxonomy (/tags/awesome/index.html)

.Site.GetPage with Sections

.Kind可以轻松地与模板中的where函数结合使用,创建特定类型的内容列表。这种方法非常适合创建列表,但有时您可能想通过章节的路径获取单个章节的索引页面。

.GetPage函数查找给定Kindpath的索引页。

​ 您可以使用两个参数调用.Site.GetPagekind(上述有效Kind之一)和kind value

例如:

  • {{ .Site.GetPage "section" "posts" }}
  • {{ .Site.GetPage "page" "search" }}

示例:创建默认章节模板

layouts/_default/section.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
{{ define "main" }}
  <main>
      {{ .Content }}
          <ul class="contents">
          {{ range .Paginator.Pages }}
              <li>{{ .Title }}
                  <div>
                    {{ partial "summary.html" . }}
                  </div>
              </li>
          {{ end }}
          </ul>
      {{ partial "pagination.html" . }}
  </main>
{{ end }}

示例:使用 .Site.GetPage

.Site.GetPage 的示例假设有以下项目目录结构:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
.
└── content
    ├── blog
    │   ├── _index.md # "title: My Hugo Blog" in the front matter
    │   ├── post-1.md
    │   ├── post-2.md
    │   └── post-3.md
    └── events #Note there is no _index.md file in "events"
        ├── event-1.md
        └── event-2.md

​ 如果没有找到 _index.md 页面,则 .Site.GetPage 将返回 nil。因此,如果 content/blog/_index.md 不存在,则该模板将输出该章节的名称:

1
<h1>{{ with .Site.GetPage "section" "blog" }}{{ .Title }}{{ end }}</h1>

​ 由于 blog 有一个带有前置元数据的章节索引页位于 content/blog/_index.md,因此上述代码将返回以下结果:

1
<h1>My Hugo Blog</h1>

​ 但如果我们尝试在 events 章节使用相同的代码,则 Hugo 会默认使用章节标题,因为没有 content/events/_index.md 可供提取内容和前置元数据:

1
<h1>{{ with .Site.GetPage "section" "events" }}{{ .Title }}{{ end }}</h1>

​ 然后返回以下结果:

1
<h1>Events</h1>

另请参阅

10 - 单页模板

Single Page Templates - 单页模板

​ 在 Hugo 中,内容的主要视图是单个视图。Hugo 会为每个 Markdown 文件提供相应的单个模板进行渲染。

单页模板查找顺序

​ 请参阅模板查找

单页模板示例

​ 内容页面的类型是 page,因此可以在它们的模板中使用所有 页面变量站点变量

posts/single.html

​ 这个单页模板使用了 Hugo 的 基础模板.Format 函数 来处理日期、.WordCount 页面变量 以及遍历单一内容的特定分类法with 也用来检查是否在前置元数据中设置了分类法。

layouts/posts/single.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
{{ define "main" }}

<section id="main">
  <h1 id="title">{{ .Title }}</h1>
  <div>
    <article id="content">
      {{ .Content }}
    </article>
  </div>
</section>
<aside id="meta">
  <div>
  <section>
    <h4 id="date"> {{ .Date.Format "Mon Jan 2, 2006" }} </h4>
    <h5 id="wordcount"> {{ .WordCount }} Words </h5>
  </section>
    {{ with .GetTerms "topics" }}
      <ul id="topics">
        {{ range . }}
          <li><a href="{{ .RelPermalink }}">{{ .LinkTitle }}</a></li>
        {{ end }}
      </ul>
    {{ end }}
    {{ with .GetTerms "tags" }}
      <ul id="tags">
        {{ range . }}
          <li><a href="{{ .RelPermalink }}">{{ .LinkTitle }}</a></li>
        {{ end }}
      </ul>
    {{ end }}
  </div>
  <div>
    {{ with .PrevInSection }}
      <a class="previous" href="{{ .Permalink }}"> {{ .Title }}</a>
    {{ end }}
    {{ with .NextInSection }}
      <a class="next" href="{{ .Permalink }}"> {{ .Title }}</a>
    {{ end }}
  </div>
</aside>
{{ end }}

​ 要轻松生成一个内容类型的新实例(例如,在像 project/ 这样的章节中生成新的 .md 文件),并预先配置好前置元数据,请使用内容原型

另请参阅

11 - 内容视图模板

Content View Templates - 内容视图模板

https://gohugo.io/templates/views/

​ Hugo可以渲染内容的替代视图,这在列表和摘要视图中特别有用。

​ 这些替代的内容视图在列表模板中特别有用。

​ 以下是内容视图的常见用例:

  • 您希望在主页上显示每种类型的内容,但仅以有限的摘要视图显示。
  • 您只想在分类列表页面上显示您的内容的项目列表。视图通过将每种不同类型的内容的渲染委托给内容本身来使此过程变得非常简单。

创建内容视图

​ 要创建新视图,请在每个不同的内容类型目录中创建具有视图名称的模板。以下示例包含用于postsproject内容类型的"li"视图和"summary"视图。正如您所看到的,这些视图与单个内容视图模板single.html并排。您甚至可以为给定类型提供特定的视图,并继续使用_default/single.html作为主视图。

1
2
3
4
5
6
7
8
9
  ▾ layouts/
    ▾ posts/
        li.html
        single.html
        summary.html
    ▾ project/
        li.html
        single.html
        summary.html

​ Hugo还支持使用默认内容模板,以在没有为该类型提供特定内容视图模板的情况下使用。内容视图也可以在_default目录中定义,并且将像列表和单个模板一样工作,最终作为查找顺序的一部分向下传递到_default目录中。

1
2
3
4
5
▾ layouts/
  ▾ _default/
      li.html
      single.html
      summary.html

哪个模板将被渲染?

​ 以下是内容视图的查找顺序:

  1. /layouts/<TYPE>/<VIEW>.html
  2. /layouts/_default/<VIEW>.html
  3. /themes/<THEME>/layouts/<TYPE>/<VIEW>.html
  4. /themes/<THEME>/layouts/_default/<VIEW>.html

示例:列表中的内容视图

​ 以下示例演示了如何在列表模板中使用内容视图。

list.html

​ 在此示例中,将.Render被传递到模板中以调用render函数.Render是一种特殊的函数,它指示内容使用第一个参数提供的视图模板渲染自身。在本例中,该模板将渲染以下summary.html视图:

layouts/_default/list.html

1
2
3
4
5
6
7
8
<main id="main">
  <div>
    <h1 id="title">{{ .Title }}</h1>
    {{ range .Pages }}
      {{ .Render "summary" }}
    {{ end }}
  </div>
</main>

summary.html

​ Hugo将整个页面对象传递给以下summary.html视图模板。(有关完整列表,请参见页面变量。)

layouts/_default/summary.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<article class="post">
  <header>
    <h2><a href='{{ .Permalink }}'> {{ .Title }}</a> </h2>
    <div class="post-meta">{{ .Date.Format "Mon, Jan 2, 2006" }} - {{ .FuzzyWordCount }} Words </div>
  </header>
  {{ .Summary }}
  <footer>
  <a href='{{ .Permalink }}'><nobr>Read more →</nobr></a>
  </footer>
</article>

li.html

​ 继续上一个示例,我们可以通过更改调用 .Render 函数中的参数来使用较小的 li.html 视图(即 {{ .Render "li" }})。

layouts/_default/li.html

1
2
3
4
<li>
  <a href="{{ .Permalink }}">{{ .Title }}</a>
  <div class="meta">{{ .Date.Format "Mon, Jan 2, 2006" }}</div>
</li>

另请参阅

12 - 数据模板

Data Templates - 数据模板

https://gohugo.io/templates/data-templates/

​ 除了Hugo内置的变量,您可以在模板或简码中指定自己的自定义数据,这些数据可以来自本地和动态源。

​ Hugo支持从位于Hugo项目根目录下的data目录中的YAML、JSON、XML和TOML文件加载数据。

data文件夹

data 文件夹应该存储在生成站点时 Hugo 需要使用的其他数据。

​ 数据文件不用于生成独立的页面。它们应该通过以下方式补充内容文件:

  • 当前置元数据字段过于复杂时扩展内容;或
  • 在模板中显示一个更大的数据集(参见下面的示例)

​ 在这两种情况下,最好将这些数据外包到它们(自己的)的文件中。

​ 这些文件必须是 YAML、JSON、XML 或 TOML 文件(使用 .yml.yaml.json.xml.toml 扩展名)。这些数据将作为 map 存储在 .Site.Data 变量中。

​ 要使用 site.Data.filename 表示法访问数据,该filename必须以下划线或 Unicode 字母开头,后跟零个或多个下划线、Unicode 字母或 Unicode 数字。例如:

  • 123.json - 无效的
  • x123.json - 有效的
  • _123.json - 有效的

​ 要使用 index 函数访问这些数据,则该文件名无关紧要。例如:

数据文件模板代码
123.json{{ index .Site.Data "123" }}
x123.json{{ index .Site.Data "x123" }}
_123.json{{ index .Site.Data "_123" }}
x-123.json{{ index .Site.Data "x-123" }}

主题中的数据文件

​ 数据文件也可以在主题中使用。

​ 但是,请注意,主题数据文件与项目目录合并,以项目目录为优先。也就是说,如果存在相同名称和相对路径的两个文件,则根项目 data 目录中文件中的数据将覆盖 themes/<THEME>/data 目录中文件中的数据(对于重复的键)。

​ 因此,主题作者应该小心,不要包含用户可以轻松覆盖的数据文件,因为用户可能决定自定义主题。对于不应被覆盖的特定于主题的数据项,最好在文件夹结构前加上命名空间,例如 mytheme/data/<THEME>/somekey/...。要检查是否存在此类重复项,请使用 -v 标志运行 hugo。

​ 从数据文件创建数据模板的映射中的键将是一组点链接的 pathfilename 和文件中的 key(如果适用)。

​ 以下是最好的说明例子:

示例:Jaco Pastorius的个人唱片

Jaco Pastorius 是一位伟大的贝斯手,但他的个人唱片分类目录非常简短,足以作为一个示例。John Patitucci 是另一位贝斯巨匠。

​ 下面的示例有点牵强,但它说明了数据文件的灵活性。这个示例使用 TOML 作为文件格式,其中包含以下两个数据文件:

  • data/jazz/bass/jacopastorius.toml
  • data/jazz/bass/johnpatitucci.toml

jacopastorius.toml 包含以下内容。 johnpatitucci.toml 包含类似的列表:

jacopastorius.

=== “yaml”

``` yaml
discography:
- 1974 - Modern American Music … Period! The Criteria Sessions
- 1974 - Jaco
- 1976 - Jaco Pastorius
- 1981 - Word of Mouth
- 1981 - The Birthday Concert (released in 1995)
- 1982 - Twins I & II (released in 1999)
- 1983 - Invitation
- 1986 - Broadway Blues (released in 1998)
- 1986 - Honestly Solo Live (released in 1990)
- 1986 - Live In Italy (released in 1991)
- 1986 - Heavy'n Jazz (released in 1992)
- 1991 - Live In New York City, Volumes 1-7.
- 1999 - Rare Collection (compilation)
- '2003 - Punk Jazz: The Jaco Pastorius Anthology (compilation)'
- 2007 - The Essential Jaco Pastorius (compilation)
```

=== “toml”

``` toml
discography = ['1974 - Modern American Music … Period! The Criteria Sessions', '1974 - Jaco', '1976 - Jaco Pastorius', '1981 - Word of Mouth', '1981 - The Birthday Concert (released in 1995)', '1982 - Twins I & II (released in 1999)', '1983 - Invitation', '1986 - Broadway Blues (released in 1998)', '1986 - Honestly Solo Live (released in 1990)', '1986 - Live In Italy (released in 1991)', "1986 - Heavy'n Jazz (released in 1992)", '1991 - Live In New York City, Volumes 1-7.', '1999 - Rare Collection (compilation)', '2003 - Punk Jazz: The Jaco Pastorius Anthology (compilation)', '2007 - The Essential Jaco Pastorius (compilation)']
```

=== “json”

``` json
{
   "discography": [
      "1974 - Modern American Music … Period! The Criteria Sessions",
      "1974 - Jaco",
      "1976 - Jaco Pastorius",
      "1981 - Word of Mouth",
      "1981 - The Birthday Concert (released in 1995)",
      "1982 - Twins I \u0026 II (released in 1999)",
      "1983 - Invitation",
      "1986 - Broadway Blues (released in 1998)",
      "1986 - Honestly Solo Live (released in 1990)",
      "1986 - Live In Italy (released in 1991)",
      "1986 - Heavy'n Jazz (released in 1992)",
      "1991 - Live In New York City, Volumes 1-7.",
      "1999 - Rare Collection (compilation)",
      "2003 - Punk Jazz: The Jaco Pastorius Anthology (compilation)",
      "2007 - The Essential Jaco Pastorius (compilation)"
   ]
}
```

​ 可以通过 .Site.Data.jazz.bass 访问贝斯手列表,通过添加文件名而不带后缀名来访问单个贝斯手,例如 .Site.Data.jazz.bass.jacopastorius

​ 现在可以在模板中呈现所有贝斯手的唱片列表:

1
2
3
{{ range $.Site.Data.jazz.bass }}
   {{ partial "artist.html" . }}
{{ end }}

​ 然后在 partials/artist.html 中:

1
2
3
4
5
<ul>
{{ range .discography }}
  <li>{{ . }}</li>
{{ end }}
</ul>

​ 发现新的喜欢的贝斯手?只需在相同的目录中添加另一个 .toml 文件即可。

示例:从数据文件中访问命名的值

​ 假设在 data/ 下的 User0123.[yml|toml|xml|json] 数据文件中,您有以下数据结构:

User0123.

=== “yaml”

``` yaml
Achievements:
- Can create a Key, Value list from Data File
- Learns Hugo
- Reads documentation
Name: User0123
Short Description: He is a **jolly good** fellow.
```

=== “toml”

``` toml
Achievements = ['Can create a Key, Value list from Data File', 'Learns Hugo', 'Reads documentation']
Name = 'User0123'
'Short Description' = 'He is a **jolly good** fellow.'
```

=== “json”

``` json
{
   "Achievements": [
      "Can create a Key, Value list from Data File",
      "Learns Hugo",
      "Reads documentation"
   ],
   "Name": "User0123",
   "Short Description": "He is a **jolly good** fellow."
}
```

​ 您可以使用以下代码在布局中渲染 Short Description

1
<div>Short Description of {{ .Site.Data.User0123.Name }}: <p>{{ index .Site.Data.User0123 "Short Description" | markdownify }}</p></div>

​ 请注意使用 markdownify 模板函数。这将通过 Markdown 渲染引擎发送描述。

获取远程数据

​ 使用 getJSONgetCSV 获取远程数据:

1
2
{{ $dataJ := getJSON "url" }}
{{ $dataC := getCSV "separator" "url" }}

​ 如果为 URL 使用前缀或后缀,则这些函数接受可变参数

1
2
{{ $dataJ := getJSON "url prefix" "arg1" "arg2" "arg n" }}
{{ $dataC := getCSV  "separator" "url prefix" "arg1" "arg2" "arg n" }}

getCSV 的分隔符(separator)必须放在第一个位置,并且只能是一个字符长。

​ 所有传递的参数将连接到最终 URL:

1
2
{{ $urlPre := "https://api.github.com" }}
{{ $gistJ := getJSON $urlPre "/users/GITHUB_USERNAME/gists" }}

​ 这将在内部解析为以下内容:

1
{{ $gistJ := getJSON "https://api.github.com/users/GITHUB_USERNAME/gists" }}

添加 HTTP 标头

getJSONgetCSV 都以可选的 map 作为最后一个参数,例如:

1
{{ $data := getJSON "https://example.org/api" (dict "Authorization" "Bearer abcd") }}

​ 如果您需要同一标头键的多个值,请使用切片:

1
{{ $data := getJSON "https://example.org/api" (dict "X-List" (slice "a" "b" "c")) }}

CSV 文件示例

​ 对于getCSV,一个字符长的分隔符必须放在第一个位置,然后是URL。以下是从已发布的CSV在partial模板中创建HTML表格的示例:

layouts/partials/get-csv.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
  <table>
    <thead>
      <tr>
      <th>Name</th>
      <th>Position</th>
      <th>Salary</th>
      </tr>
    </thead>
    <tbody>
    {{ $url := "https://example.com/finance/employee-salaries.csv" }}
    {{ $sep := "," }}
    {{ range $i, $r := getCSV $sep $url }}
      <tr>
        <td>{{ index $r 0 }}</td>
        <td>{{ index $r 1 }}</td>
        <td>{{ index $r 2 }}</td>
      </tr>
    {{ end }}
    </tbody>
  </table>

​ 表达式{{ index $r number }}必须用于输出当前行的第n列。

缓存 URL

​ 每个下载的URL将被缓存到默认文件夹$TMPDIR/hugo_cache/中。变量$TMPDIR将被解析为依赖于您系统的临时目录。

​ 使用命令行标志--cacheDir,您可以指定系统上的任何文件夹作为缓存目录。

​ 您还可以在主配置文件中设置cacheDir

​ 如果您不喜欢缓存,可以使用命令行标志--ignoreCache完全禁用缓存。

使用 REST URL 进行身份验证

​ 目前,您只能使用可以放入URL中的那些身份验证方法。OAuth和其他身份验证方法未实现。

加载本地文件

​ 要使用getJSONgetCSV加载本地文件,源文件必须位于Hugo的工作目录中。文件扩展名不重要,但(文件的)内容重要。

​ 它应用了与上面在获取远程数据中相同的输出逻辑。

​ 要使用getCSV加载的本地CSV文件必须位于data目录之外

数据文件的 LiveReload

​ 当URL的内容发生更改时,没有机会触发LiveReload。但是,当本地文件更改时(即,data/*themes/<THEME>/data/*),将触发LiveReload。不支持符号链接。请注意,由于下载数据需要一段时间,Hugo会在数据下载完成之前停止处理Markdown文件。

​ 如果更改了任何本地文件并触发了LiveReload,则Hugo将从缓存中读取数据驱动(URL)内容。如果您禁用了缓存(例如,通过使用hugo server --ignoreCache运行服务器),Hugo将在每次LiveReload触发时重新下载内容。这可能会产生巨大的流量。您可能会很快达到API限制。

数据驱动内容的示例

数据格式规范

另请参阅

13 - 局部模板

Partial Templates - 局部模板

https://gohugo.io/templates/partials/

​ Partial 是在列表和页面模板中使用的更小的上下文感知组件,可以经济地使用以保持模板 DRY。

局部模板查找顺序

​ Partial 模板(如单页面模板列表页面模板)具有特定的查找顺序。然而,partial 更简单,因为 Hugo 只会检查两个地方:

  1. layouts/partials/*<PARTIALNAME>.html
  2. themes/<THEME>/layouts/partials/*<PARTIALNAME>.html

​ 这允许某一主题的最终用户将 partial 的内容复制到同名文件中以进行进一步的自定义

在您的模板中使用 Partial

​ Hugo 项目中的所有 partial 都位于一个名为 layouts/partials 的目录中。为了更好的组织,您还可以在 partials 中创建多个子目录:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
layouts/
└── partials/
    ├── footer/
    │   ├── scripts.html
    │   └── site-footer.html
    ├── head/
    │   ├── favicons.html
    │   ├── metadata.html
    │   ├── prerender.html
    │   └── twitter.html
    └── header/
        ├── site-header.html
        └── site-nav.html

​ 在模板中调用所有 partial 都使用以下模式:

1
{{ partial "<PATH>/<PARTIAL>.html" . }}

​ 新手 Hugo 用户最常见的错误之一是未能向 partial 调用传递上下文。在上述模式中,请注意如何使用“点号”(.)作为第二个参数来给出 partial 上下文。您可以在Hugo 模板介绍中了解更多有关"the dot"的信息。

<PARTIAL>包括baseof已被保留。(#5373

​ 如上例目录结构所示,您可以在partials中嵌套目录以获得更好的源代码组织。您只需要使用相对于partials目录的嵌套 partial 路径即可:

1
2
{{ partial "header/site-header.html" . }}
{{ partial "footer/scripts.html" . }}

变量作用域

​ partial 调用中的第二个参数是要传递下去的变量。上述示例传递了.,这告诉接收 partial 的模板应用当前上下文

​ 这意味着 partial 只能访问这些变量。partial 是被隔离的,无法访问外部作用域。在 partial 内部,$.Var 等同于 .Var

从 Partial 返回一个值

​ 除了输出标记之外,partial 还可以用于返回任何类型的值。为了返回一个值,partial 必须在partial 的末尾包括一个孤立的 return 语句。

示例 GetFeatured

1
2
3
4
5
6
{{/* layouts/partials/GetFeatured.html */}}
{{ return first . (where site.RegularPages "Params.featured" true) }}
{{/* layouts/index.html */}}
{{ range partial "GetFeatured.html" 5 }}
  [...]
{{ end }}

示例 GetImage

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
{{/* layouts/partials/GetImage.html */}}
{{ $image := false }}
{{ with .Params.gallery }}
  {{ $image = index . 0 }}
{{ end }}
{{ with .Params.image }}
  {{ $image = . }}
{{ end }}
{{ return $image }}
{{/* layouts/_default/single.html */}}
{{ with partial "GetImage.html" . }}
  [...]
{{ end }}

​ 每个 partial 文件只允许一个 return 语句。

内联 Partial

​ 您还可以在模板中内联定义 partial。但是请记住,模板命名空间是全局的,因此您需要确保名称是唯一的,以避免冲突。

1
2
3
4
5
6
Value: {{ partial "my-inline-partial.html" . }}

{{ define "partials/my-inline-partial.html" }}
{{ $value := 32 }}
{{ return $value }}
{{ end }}

缓存的 Partials

partialCached模板函数可以为不需要在每次调用时重新渲染的复杂模板提供显著的性能提升。最简单的用法如下:

1
{{ partialCached "footer.html" . }}

​ 您也可以传递附加参数给partialCached,以创建缓存 partial 模板的变体

​ 例如,您可以告诉Hugo只对每个章节渲染一次footer.html partial 模板:

1
{{ partialCached "footer.html" . .Section }}

​ 如果您需要传递额外的参数以创建唯一的变体,您可以传递任意数量的变体参数:

1
{{ partialCached "footer.html" . .Params.country .Params.province }}

​ 注意,变体参数不会被传递给底层的 partial 模板,它们只用于创建唯一的缓存键。

示例 header.html

​ 下面的header.html partial 模板被用于spf13.com

layouts/partials/header.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<!DOCTYPE html>
<html class="no-js" lang="en-US" prefix="og: http://ogp.me/ns# fb: http://ogp.me/ns/fb#">
<head>
    <meta charset="utf-8">

    {{ partial "meta.html" . }}

    <base href="{{ .Site.BaseURL }}">
    <title> {{ .Title }} : spf13.com </title>
    <link rel="canonical" href="{{ .Permalink }}">
    {{ if .RSSLink }}<link href="{{ .RSSLink }}" rel="alternate" type="application/rss+xml" title="{{ .Title }}" />{{ end }}

    {{ partial "head_includes.html" . }}
</head>

header.html 这个示例的partial 是在 Hugo 引入 block templates 之前创建的。关于如何定义主模板(例如站点的头部、页头和页脚)的外部 chrome 或 shell,可以在 base templates and blocks 中了解更多信息。您甚至可以组合使用 blocks 和 partials,以增加灵活性。

示例 footer.html

​ 下面的footer.html partial 模板被用于spf13.com

layouts/partials/footer.html

1
2
3
4
5
6
7
8
9
<footer>
  <div>
    <p>
    &copy; 2013-14 Steve Francia.
    <a href="https://creativecommons.org/licenses/by/3.0/" title="Creative Commons Attribution">Some rights reserved</a>;
    please attribute properly and link back.
    </p>
  </div>
</footer>

另请参阅

14 - 创建自己的简码

Create Your Own Shortcodes - 创建自己的简码

https://gohugo.io/templates/shortcode-templates/

​ 您可以使用与单页和列表页相同的模板语法来创建自己的简码,以扩展Hugo内置的简码。

​ 简码是一种将模板合并成小型、可重用的代码片段的方式,您可以直接在内容中嵌入这些代码片段。从这个意义上说,您可以将简码视为页面和列表模板基本内容文件之间的中间件。

​ Hugo还提供了常见用例的内置简码。(参见内容管理:简码。)

创建自定义Shortcodes

​ Hugo的内置简码涵盖了许多常见但不是全部的用例。幸运的是,Hugo提供了轻松创建自定义简码以满足您站点需求的功能。

文件位置

​ 要创建一个简码,请在 源文件组织layouts/shortcodes 目录中放置一个 HTML 模板。请仔细考虑文件名,因为简码名称将与文件名相同,但没有 .html 扩展名。例如,layouts/shortcodes/myshortcode.html 将根据您选择的参数类型使用 \{\{\< myshortcode /\>\}\}\{\{\% myshortcode \/\%\}\} 进行调用。

​ 您可以在子文件夹中组织您的简码,例如在 layouts/shortcodes/boxes 中。然后,这些简码将使用它们的相对路径进行访问,例如:

1
\{{\< boxes/square \>\}\}

​ 注意正斜杠。

简码模板查找顺序

​ 简码模板具有简单的查找顺序

  1. /layouts/shortcodes/<SHORTCODE>.html
  2. /themes/<THEME>/layouts/shortcodes/<SHORTCODE>.html

位置参数 vs 命名参数

​ 您可以使用以下类型的参数创建简码:

  • 位置参数
  • 命名参数
  • 位置或命名参数(即"灵活(flexible)")

​ 在具有位置参数的简码中,参数的顺序很重要。如果简码有一个必需的单一值(例如下面的youtube简码),则位置参数非常有效,并且需要的内容作者输入较少。

​ 对于具有多个或可选参数的更复杂的布局,命名参数效果最好。虽然不太简洁,但命名参数需要较少的内容作者记忆,并且可以按任意顺序添加到简码声明中。

​ 允许两种类型的参数(即"灵活(flexible)“的简码)对于复杂的布局非常有用,您可以设置默认值,这些默认值可以很容易地被用户覆盖。

访问参数

​ 可以通过 .Get 方法访问所有简码参数。无论是将键(即字符串)还是数字传递给 .Get 方法取决于您是否正在访问命名或位置参数。

​ 要通过名称访问参数,请使用.Get方法,后跟命名参数作为引用字符串的形式:

1
{{ .Get "class" }}

​ 要通过位置访问参数,请使用 .Get,后跟数字位置,要记住位置参数是从零开始编号的:

1
{{ .Get 0 }}

​ 对于第二个位置,您只需要使用:

1
{{ .Get 1 }}

​ 当输出取决于参数是否设置时,使用with 很棒:

1
{{ with .Get "class" }} class="{{ . }}"{{ end }}

.Get 也可以用于检查是否已提供参数。当条件取决于两个值中的任一个或两个值时,这非常有用:

1
{{ if or (.Get "title") (.Get "alt") }} alt="{{ with .Get "alt" }}{{ . }}{{ else }}{{ .Get "title" }}{{ end }}"{{ end }}

.Inner

​ 如果使用了闭合的简码,.Inner 变量将被填充为开放和闭合简码之间的内容。如果需要闭合的简码,则可以检查 .Inner 的长度以指示其存在。

​ 通过.Inner变量声明内容的简码也可以使用自闭合语法来声明,而无需内容和结束标签:

1
\{\{\< innershortcode \/\>\}\}

任何引用.Inner的简码都必须是闭合的或自闭合的。

.Params

​ 简码中的.Params变量包含传递给简码的参数列表,用于更复杂的用例。您也可以使用以下逻辑访问更高级别的参数:

  • $.Params

    这些是直接传递到简码声明中的参数(例如,YouTube视频ID)

  • $.Page.Params

    引用该页面的参数;在这种情况下, 该"page"指的是声明简码的内容文件(例如,内容的前置元数据中的shortcode_color字段可以通过$.Page.Params.shortcode_color访问)。

  • $.Page.Site.Params

    引用您站点配置文件中定义的全局变量。

.IsNamedParams

.IsNamedParams 变量检查简码声明是否使用了命名参数,并返回一个布尔值。

​ 例如,您可以创建一个 image 简码,可以使用命名参数 src 或第一个位置参数,具体取决于内容作者的偏好。假设 image 简码的调用方式如下:

1
\{\{\< image src="images/my-image.jpg" \>\}\}

​ 然后,您可以将以下内容包含在您的简码模板中:

1
2
3
4
5
{{ if .IsNamedParams }}
<img src="{{ .Get "src" }}" alt="">
{{ else }}
<img src="{{ .Get 0 }}" alt="">
{{ end }}

​ 请查看下面的 Vimeo 简码示例 以了解 .IsNamedParams 的用法。

​ 虽然可以创建接受位置参数和命名参数的简码模板,但是在内容中声明的简码不能混合参数类型。因此,像 \{\{\< image src="images/my-image.jpg" "This is my alt text" \>\}\} 这样声明的简码将返回一个错误。

​ 您还可以使用变量.Page访问所有普通页面变量

​ 简码也可以嵌套。在嵌套的简码标签中,您可以使用 .Parent 变量 访问父级简码的上下文,这对于从根继承常见的简码参数非常有用。

检查是否存在

​ 您可以通过在该页面模板中调用.HasShortcode,并提供简码的名称来检查页面上是否使用了特定的简码。当您想要在头部中包含仅由该简码使用的特定脚本或样式时,这一功能有时很有用。

自定义简码示例

​ 以下是通过在/layouts/shortcodes中的简码模板文件创建的不同类型的简码示例。

单词示例:year

​ 假设您想在不需要不断查看 Markdown 的情况下,在内容文件中保持版权年份的更新。您的目标是可以按照以下方式调用简码:

1
\{\{\< year \>\}\}

/layouts/shortcodes/year.html

1
{{ now.Format "2006" }}

单位置示例:YouTube

​ 内嵌视频是 Markdown 内容中常见的补充,但很容易变得不美观。以下是 Hugo 的内置 YouTube 简码 使用的代码:

1
\{\{\< youtube 09jf3ow9jfw \>\}\}

​ 将会加载 /layouts/shortcodes/youtube.html 中的模板:

/layouts/shortcodes/youtube.html

1
2
3
4
<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>

youtube-embed.html

1
2
3
4
5
6
7
<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 简码。您的目标是可以在内容文件中按照以下方式调用简码:

content-image.md

1
\{\{\< img src="/media/spf13.jpg" title="Steve Francia" \>\}\}

​ 您已经在 /layouts/shortcodes/img.html 中创建了简码,它会加载以下简码模板:

/layouts/shortcodes/img.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
<!-- 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 -->

​ 将被渲染为:

img-output.html

1
2
3
4
5
6
<figure>
  <img src="/media/spf13.jpg"  />
  <figcaption>
      <h4>Steve Francia</h4>
  </figcaption>
</figure>

单灵活示例:vimeo

1
2
\{\{\< vimeo 49718712 \>\}\}
\{\{\< vimeo id="49718712" class="flex-video" \>\}\}

​ 将会加载 /layouts/shortcodes/vimeo.html 中的模板:

/layouts/shortcodes/vimeo.html

1
2
3
4
5
6
7
8
9
{{ 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 }}

​ 将被渲染为:

vimeo-iframes.html

1
2
3
4
5
6
<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-example.md

1
2
3
4
5
\{\{\< highlight html \>\}\}
  <html>
    <body> This HTML </body>
  </html>
\{\{\< /highlight \>\}\}

highlight 简码的模板使用以下代码,它已经包含在 Hugo 中:

1
{{ .Get 0 | highlight .Inner }}

​ HTML 示例代码块的渲染输出如下:

syntax-highlighted.html

1
2
3
4
<div class="highlight" style="background: #272822"><pre style="line-height: 125%"><span style="color: #f92672">&lt;html&gt;</span>
    <span style="color: #f92672">&lt;body&gt;</span> This HTML <span style="color: #f92672">&lt;/body&gt;</span>
<span style="color: #f92672">&lt;/html&gt;</span>
</pre></div>

嵌套的简码:图像库

​ Hugo的.Parent 简码变量简码的上下文中被调用时提供了对父简码上下文的访问,这为常见的简码参数提供了继承模型。

​ 下面的示例是人为的,但演示了该概念。假设您有一个 gallery 简码,它期望一个名为 class 的参数:

layouts/shortcodes/gallery.html

1
2
3
<div class="{{ .Get "class" }}">
  {{ .Inner }}
</div>

​ 您还有一个 img 简码,只有一个名为 src 的参数,您想在 gallery 和其他简码中调用它,以使父级定义每个 img 的上下文:

layouts/shortcodes/img.html

1
2
3
4
5
6
{{- $src := .Get "src" -}}
{{- with .Parent -}}
  <img src="{{ $src }}" class="{{ .Get "class" }}-image">
{{- else -}}
  <img src="{{ $src }}">
{{- end -}}

​ 然后您可以在内容中按以下方式调用您的简码:

1
2
3
4
5
\{\{\< gallery class="content-gallery" \>\}\}
  \{\{\< img src="/images/one.jpg" \>\}\}
  \{\{\< img src="/images/two.jpg" \>\}\}
\{\{\< /gallery \>\}\}
\{\{\< img src="/images/three.jpg" \>\}\}

​ 这将输出以下 HTML。请注意,前两个 img 简码继承了通过调用父级 gallery 设置的 class 值为 content-gallery,而第三个 img 只使用了 src

1
2
3
4
5
<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 模板函数和 .Position 变量可获得简码中有用的错误消息:

1
2
3
4
{{ with .Get "name" }}
{{ else }}
{{ errorf "missing value for param 'name': %s" .Position }}
{{ end }}

​ 当上述失败时,您会看到类似下面的 ERROR 日志:

1
ERROR 2018/11/07 10:05:55 missing value for param name: "/Users/bep/dev/go/gohugoio/hugo/docs/content/en/variables/shortcodes.md:32:1"

更多简码示例

​ 更多简码示例可以在 spf13.com 的简码目录Hugo 文档的简码目录 中找到。

内联简码

​ 您也可以内联实现您的简码——例如,在您使用它们的内容文件中。这对于您只需要在一个地方使用脚本非常有用。

​ 这个功能默认是禁用的,但可以在您的站点配置中启用:

config.

=== “yaml”

``` yaml
enableInlineShortcodes: true
```

=== “toml”

``` toml
enableInlineShortcodes = true
```

=== “json”

``` json
{
   "enableInlineShortcodes": true
}
```

​ 它出于安全原因默认禁用。Hugo 模板处理使用的安全模型假定模板作者是可信的,但内容文件不是,因此模板是安全的,可以避免因输入数据格式不正确而出现注入问题。但在大多数情况下,您也可以完全控制内容,那么 enableInlineShortcodes = true 将被认为是安全的。但要注意:它允许从内容文件中执行 ad-hoc Go 文本模板

​ 启用后,您可以在内容文件中执行以下操作:

1
\{\{\< time.inline \>\}\}{{ now }}\{\{\< /time.inline \>\}\}

​ 上述代码将打印当前日期和时间。

请注意,内联简码的内部内容将被解析并作为一个具有与常规简码模板相同上下文的 Go 文本模板执行。

​ 这意味着可以通过.Page.Title等方式访问当前页面。这也意味着没有"嵌套内联简码"的概念。

​ 同一个内联简码可以在同一个内容文件中多次重复使用,如果需要不同的参数,则使用自闭合语法:

1
\{\{\< time.inline /\>\}\}

另请参阅

15 - 本地文件模板

Local File Templates - 本地文件模板

https://gohugo.io/templates/files/

​ Hugo 的 readDirreadFile 函数使得遍历项目目录结构和将文件内容写入模板变得容易。

遍历本地文件

​ 使用 Hugo 的 readDirreadFile 模板函数,您可以遍历服务器上站点的文件。

使用 readDir

readDir 函数 返回一个由 os.FileInfo 组成的数组。它以文件的 path 作为单个字符串参数。这个路径可以指向您站点上的任何目录(即服务器文件系统中的目录)。

​ 路径是绝对还是相对并不重要,因为对于 readDir 函数,您站点的根目录(通常是 ./public/)实际上同时扮演两个角色:

  1. 文件系统根目录
  2. 当前工作目录

使用 readFile

readfile 函数 从磁盘读取文件并将其转换为字符串,以便由其他 Hugo 函数操纵或按原样添加。readFile 将文件(包括路径)作为传递给该函数的参数。

​ 在模板中使用 readFile 函数时,请确保路径相对于Hugo 项目根目录

1
{{ readFile "/content/templates/local-file-templates" }}

readFile 示例:将项目文件添加到内容

​ 由于 readFile 是一个函数,因此它仅在模板中可用,而不在内容中可用。然而,我们可以创建一个简单的 简码模板,来调用 readFile,将第一个参数通过该函数传递,然后允许一个可选的第二个参数将文件通过 Markdown 处理器。将这个 简码添加到内容中的模式如下:

1
\{\{\< readfile file="/path/to/local/file.txt" markdown="true" \>\}\}

​ 如果要使用 readFile 为主题创建自定义简码,请注意,简码的使用将参考项目根目录,而不是您的 themes 目录。

另请参阅

16 - 自定义404页面

Custom 404 Page - 自定义404页面

https://gohugo.io/templates/404/

​ 如果您知道如何创建单页模板,那么您可以无限制地创建自定义404页面。

​ 当使用 Hugo 与 GitHub Pages 时,可以通过在 layouts 文件夹的根目录中创建 404.html 模板文件来提供 自定义的404 错误页面。当 Hugo 生成您的站点时,404.html 文件将被放置在根目录中。

​ 404 页面将拥有可用于模板的所有常规页面变量

​ 除了标准页面变量外,404 页面还可以从 .Pages 访问所有站点内容。

1
2
▾ layouts/
    404.html

404.html

​ 这是一个基本的404.html模板示例:

​ 以下是一个基本的 404.html 模板示例:

layouts/404.html

1
2
3
4
5
6
7
{{ define "main" }}
  <main id="main">
    <div>
      <h1 id="title"><a href="{{ "" | relURL }}">Go Home</a></h1>
    </div>
  </main>
{{ end }}

自动加载

​ 您的 404.html 文件可以在访问者输入错误的 URL 路径时自动加载,具体取决于您正在使用的 Web 服务器环境。例如:

  • GitHub PagesGitLab Pages。404 页面是自动的。
  • Apache。您可以在站点根目录的 .htaccess 文件中指定 ErrorDocument 404 /404.html
  • Nginx。您可以在 nginx.conf 文件中指定 error_page 404 /404.html;详情在此
  • Amazon AWS S3。在为静态 Web 服务设置存储桶时,您可以从 S3 GUI 中指定错误文件。
  • Amazon CloudFront。您可以在 CloudFront 控制台的错误页面章节指定页面。详情在此
  • Caddy Server。使用 handle_errors 指令为一个或多个状态码指定错误页面。详情在此
  • Netlify。在 content/_redirects 中添加 /* /404.html 404详情在此
  • Azure Static Web App。在配置文件 staticwebapp.config.json 中设置 responseOverrides.404.rewriteresponseOverrides.404.statusCode详情在此
  • Azure Storage 作为静态站点托管。您可以在 Azure 门户的静态站点配置页中指定Error document path详情在此
  • DigitalOcean App 平台。您可以在应用程序规范文件中指定 error_document 或使用控制面板设置错误文档。详情在此
  • Firebase Hosting/404.html 自动用作404页面。

hugo server 不会自动加载您的自定义 404.html 文件,但是您可以通过将浏览器导航到/404.html来测试您的自定义"not found"页面的外观。

17 - 菜单模板

Menu Templates - 菜单模板

https://gohugo.io/templates/menu-templates/

​ 在您的模板中使用菜单变量和方法来渲染菜单。

概述

​ 在定义菜单条目之后,使用菜单变量和方法来渲染菜单。

​ 有三个因素决定如何渲染菜单:

  1. 定义菜单条目的方法:自动定义在前置元数据中定义在站点配置中定义
  2. 菜单结构:平面或嵌套
  3. 用于本地化菜单条目的方法:站点配置或翻译表

​ 下面的示例处理了每种组合。

示例

​ 这个局部模板递归地"遍历"菜单结构,渲染本地化、可访问的嵌套列表。

layouts/partials/menu.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
{{- $page := .page }}
{{- $menuID := .menuID }}

{{- with index site.Menus $menuID }}
  <nav>
    <ul>
      {{- partial "inline/menu/walk.html" (dict "page" $page "menuEntries" .) }}
    </ul>
  </nav>
{{- end }}

{{- define "partials/inline/menu/walk.html" }}
  {{- $page := .page }}
  {{- range .menuEntries }}
    {{- $attrs := dict "href" .URL }}
    {{- if $page.IsMenuCurrent .Menu . }}
      {{- $attrs = merge $attrs (dict "class" "active" "aria-current" "page") }}
    {{- else if $page.HasMenuCurrent .Menu .}}
      {{- $attrs = merge $attrs (dict "class" "ancestor" "aria-current" "true") }}
    {{- end }}
    <li>
      <a
        {{- range $k, $v := $attrs }}
          {{- with $v }}
            {{- printf " %s=%q" $k $v | safeHTMLAttr }}
          {{- end }}
        {{- end -}}
      >{{ or (T .Identifier) .Name | safeHTML }}</a>
      {{- with .Children }}
        <ul>
          {{- partial "inline/menu/walk.html" (dict "page" $page "menuEntries" .) }}
        </ul>
      {{- end }}
    </li>
  {{- end }}
{{- end }}

​ 调用上面的局部,传递一个菜单ID和当前页面的上下文。

layouts/_default/single.html

1
2
{{ partial "menu.html" (dict "menuID" "main" "page" .) }}
{{ partial "menu.html" (dict "menuID" "footer" "page" .) }}

页面引用

​ 无论您如何定义菜单条目,与页面相关联的条目都可以访问页面变量和方法。

​ 这个简单的示例在每个条目的name旁边渲染一个名为version的页面参数。使用withif来处理(a) 指向外部资源的条目,或者(b) version参数未定义的条目。

layouts/_default/single.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{{- range site.Menus.main }}
  <a href="{{ .URL }}">
    {{ .Name }}
    {{- with .Page }}
      {{- with .Params.version -}}
        ({{ . }})
      {{- end }}
    {{- end }}
  </a>
{{- end }}

菜单条目参数

当您在站点配置或前置元数据中定义菜单条目时,可以包括params键,如以下示例所示:

​ 当您在站点配置中定义菜单条目或在前置元数据中定义菜单条目时,您可以像这些示例中那样包含一个params键:

​ 这个简单的示例为每个锚点元素呈现一个class属性。使用withif来处理params.class未定义的条目。

layouts/partials/menu.html

1
2
3
4
5
{{- range site.Menus.main }}
  <a {{ with .Params.class -}} class="{{ . }}" {{ end -}} href="{{ .URL }}">
    {{ .Name }}
  </a>
{{- end }}

本地化

​ Hugo提供了两种本地化菜单条目的方法。详见多语言

另请参阅

18 - 分页

Pagination - 分页

​ Hugo支持对主页、章节页面和分类目录进行分页。

​ Hugo分页功能真正的强大之处在于与where函数及其类似SQL的操作符:firstlastafter相结合使用。您甚至可以按照Hugo中熟悉的方式对内容进行排序

配置分页

​ 可以在站点配置中配置分页:

  • paginate

    默认值为10。这个设置可以在模板中被覆盖。

  • paginatePath

    默认值为page。允许您为您的分页页面设置不同的路径。

​ 将paginate设置为正值将把主页、章节和分类列表页面拆分为这个大小的块。但是请注意,对于章节、分类和主页的分页页面的生成是惰性的——如果没有通过.Paginator引用,这些页面将不会被创建(见下文)。

paginatePath用于调整分页器中页面的URL(默认设置会生成这样的URL形式/page/1/)。

列出分页器页面

​ 提供.Paginator帮助您构建分页器菜单。此功能目前仅在主页和列表页面(即分类和章节列表)上受支持。

​ 有两种配置和使用.Paginator的方法:

  1. 最简单的方法是只需从模板中调用.Paginator.Pages。它将包含该页面的页面。
  2. 使用可用的模板函数和排序选项选择另一组页面,并将切片传递给.Paginate,例如
  • {{ range (.Paginate ( first 50 .Pages.ByTitle )).Pages }}
  • {{ range (.Paginate .RegularPagesRecursive).Pages }}.

​ 对于给定的页面,它是上面选项之一。.Paginator是静态的,一旦创建就不能更改。

​ 如果在同一页中多次调用 .Paginator.Paginate,您应该确保所有调用都是相同的。一旦在生成页面时调用了 .Paginator.Paginate,其结果就会被缓存,任何后续相似的调用都将重用缓存的结果。这意味着任何不符合第一个调用的这种调用都不会按预期行事。

(请记住,函数参数是急切地求值的,因此像 $paginator := cond x .Paginator (.Paginate .RegularPagesRecursive) 这样的调用就是您不应该做的事情。请使用 if/else 确保恰好有一个求值。)

​ 全局页面大小设置(Paginate)可以通过提供正整数作为最后一个参数来覆盖。下面的示例将每页显示五个项:

  • {{ range (.Paginator 5).Pages }}
  • {{ $paginator := .Paginate (where .Pages "Type" "posts") 5 }}

也可以将 GroupBy 函数与分页结合使用:

1
{{ range (.Paginate (.Pages.GroupByDate "2006")).PageGroups }}

构建导航

.Paginator 包含构建分页界面所需的足够信息。

​ 将内置模板(具有与 Bootstrap 兼容的样式)包含到您的页面中是添加此内容的最简单方法:

1
{{ template "_internal/pagination.html" . }}

​ 如果使用任何过滤器或排序函数来创建您的 .Paginator,并且您希望在显示页面列表之前显示导航按钮,则必须在使用之前创建 .Paginator

​ 以下示例显示如何在使用 .Paginator 之前创建 .Paginator

1
2
3
4
5
{{ $paginator := .Paginate (where .Pages "Type" "posts") }}
{{ template "_internal/pagination.html" . }}
{{ range $paginator.Pages }}
   {{ .Title }}
{{ end }}

​ 如果没有 where 过滤器,则上面的示例更加简单:

1
2
3
4
{{ template "_internal/pagination.html" . }}
{{ range .Paginator.Pages }}
   {{ .Title }}
{{ end }}

​ 如果您想要构建自定义的导航菜单,您可以使用.Paginator对象,它包括以下属性:

  • PageNumber

    当前页面在页面序列中的页码

  • URL

    当前页面器的相对URL

  • Pages

    当前页面器中的页面

  • NumberOfElements

    此页面中的元素数量

  • HasPrev

    当前页之前是否有页

  • Prev

    前一页的分页器

  • HasNext

    当前页之后是否有页

  • Next

    下一页的分页器

  • First

    第一页的分页器

  • Last

    最后一页的分页器

  • Pagers

    可用于构建分页菜单的分页器列表

  • PageSize

    每个分页器的大小

  • TotalPages

    分页器中的页面数

  • TotalNumberOfElements

    此分页器中所有页面上的元素数

附加信息

​ 页面按以下形式构建(BLANK表示没有值):

1
2
3
4
[SECTION/TAXONOMY/BLANK]/index.html
[SECTION/TAXONOMY/BLANK]/page/1/index.html => redirect to  [SECTION/TAXONOMY/BLANK]/index.html
[SECTION/TAXONOMY/BLANK]/page/2/index.html
....

另请参阅

19 - RSS模板

RSS Templates - RSS模板

https://gohugo.io/templates/rss/

​ Hugo 自带 RSS 2.0 模板,几乎不需要配置,或者您可以创建自己的 RSS 模板。

RSS模板查找顺序

​ 有关完整参考,请参见 Template Lookup Order

​ Hugo 自带了 RSS 2.0 模板。嵌入式模板对于大多数用例已经足够了。

​ RSS 页面属于 Page 类型,并且在模板中可以使用所有 页面变量

Section RSS

section 的 RSS 将在 /<SECTION>/index.xml(例如,https://spf13.com/project/index.xml)处被渲染。

​ Hugo 提供了定义任何 RSS 类型的功能,并且可以为每个章节和分类法设置不同的 RSS 文件。

RSS模板查找顺序表

​ 下表显示了不同页面类型的 RSS 模板查找顺序。第一个列表显示了在使用某一主题(demoTheme)运行时的查找顺序。

ExampleOutputFormat后缀Template Lookup Order
RSS homeRSSxml1. layouts/index.rss.xml
2. layouts/home.rss.xml
3. layouts/rss.xml
4. layouts/list.rss.xml
5. layouts/index.xml
6. layouts/home.xml
7. layouts/list.xml
8. layouts/_default/index.rss.xml
9. layouts/_default/home.rss.xml
10. layouts/_default/rss.xml
11. layouts/_default/list.rss.xml
12. layouts/_default/index.xml
13. layouts/_default/home.xml
14. layouts/_default/list.xml
15. layouts/_internal/_default/rss.xml
RSS section postsRSSxml1. layouts/posts/section.rss.xml
2. layouts/posts/rss.xml
3. layouts/posts/list.rss.xml
4. layouts/posts/section.xml
5. layouts/posts/list.xml
6. layouts/section/section.rss.xml
7. layouts/section/rss.xml
8. layouts/section/list.rss.xml
9. layouts/section/section.xml
10. layouts/section/list.xml
11. layouts/_default/section.rss.xml
12. layouts/_default/rss.xml
13. layouts/_default/list.rss.xml
14. layouts/_default/section.xml
15. layouts/_default/list.xml
16. layouts/_internal/_default/rss.xml
Taxonomy in categoriesRSSxml1. layouts/categories/category.terms.rss.xml
2. layouts/categories/terms.rss.xml
3. layouts/categories/taxonomy.rss.xml
4. layouts/categories/rss.xml
5. layouts/categories/list.rss.xml
6. layouts/categories/category.terms.xml
7. layouts/categories/terms.xml
8. layouts/categories/taxonomy.xml
9. layouts/categories/list.xml
10. layouts/category/category.terms.rss.xml
11. layouts/category/terms.rss.xml
12. layouts/category/taxonomy.rss.xml
13. layouts/category/rss.xml
14. layouts/category/list.rss.xml
15. layouts/category/category.terms.xml
16. layouts/category/terms.xml
17. layouts/category/taxonomy.xml
18. layouts/category/list.xml
19. layouts/taxonomy/category.terms.rss.xml
20. layouts/taxonomy/terms.rss.xml
21. layouts/taxonomy/taxonomy.rss.xml
22. layouts/taxonomy/rss.xml
23. layouts/taxonomy/list.rss.xml
24. layouts/taxonomy/category.terms.xml
25. layouts/taxonomy/terms.xml
26. layouts/taxonomy/taxonomy.xml
27. layouts/taxonomy/list.xml
28. layouts/_default/category.terms.rss.xml
29. layouts/_default/terms.rss.xml
30. layouts/_default/taxonomy.rss.xml
31. layouts/_default/rss.xml
32. layouts/_default/list.rss.xml
33. layouts/_default/category.terms.xml
34. layouts/_default/terms.xml
35. layouts/_default/taxonomy.xml
36. layouts/_default/list.xml
37. layouts/_internal/_default/rss.xml
Term in categoriesRSSxml1. layouts/categories/term.rss.xml
2. layouts/categories/category.rss.xml
3. layouts/categories/taxonomy.rss.xml
4. layouts/categories/rss.xml
5. layouts/categories/list.rss.xml
6. layouts/categories/term.xml
7. layouts/categories/category.xml
8. layouts/categories/taxonomy.xml
9. layouts/categories/list.xml
10. layouts/term/term.rss.xml
11. layouts/term/category.rss.xml
12. layouts/term/taxonomy.rss.xml
13. layouts/term/rss.xml
14. layouts/term/list.rss.xml
15. layouts/term/term.xml
16. layouts/term/category.xml
17. layouts/term/taxonomy.xml
18. layouts/term/list.xml
19. layouts/taxonomy/term.rss.xml
20. layouts/taxonomy/category.rss.xml
21. layouts/taxonomy/taxonomy.rss.xml
22. layouts/taxonomy/rss.xml
23. layouts/taxonomy/list.rss.xml
24. layouts/taxonomy/term.xml
25. layouts/taxonomy/category.xml
26. layouts/taxonomy/taxonomy.xml
27. layouts/taxonomy/list.xml
28. layouts/category/term.rss.xml
29. layouts/category/category.rss.xml
30. layouts/category/taxonomy.rss.xml
31. layouts/category/rss.xml
32. layouts/category/list.rss.xml
33. layouts/category/term.xml
34. layouts/category/category.xml
35. layouts/category/taxonomy.xml
36. layouts/category/list.xml
37. layouts/_default/term.rss.xml
38. layouts/_default/category.rss.xml
39. layouts/_default/taxonomy.rss.xml
40. layouts/_default/rss.xml
41. layouts/_default/list.rss.xml
42. layouts/_default/term.xml
43. layouts/_default/category.xml
44. layouts/_default/taxonomy.xml
45. layouts/_default/list.xml
46. layouts/_internal/_default/rss.xml

配置RSS

​ 默认情况下,Hugo 将创建无限数量的 RSS 条目。您可以通过在项目的 config 文件 中分配数值给 rssLimit: 字段来限制内置 RSS 模板中包含的文章数量。

​ 如果指定以下值,它们也将包含在 RSS 输出中:

config.

=== “yaml”

``` yaml
author:
  name: My Name Here
copyright: This work is licensed under a Creative Commons Attribution-ShareAlike 4.0
  International License.
languageCode: en-us
```

=== “toml”

``` toml
copyright = 'This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.'
languageCode = 'en-us'
[author]
  name = 'My Name Here'
```

=== “json”

``` json
{
   "author": {
      "name": "My Name Here"
   },
   "copyright": "This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.",
   "languageCode": "en-us"
}
```

内嵌的rss.xml

​ 以下是 Hugo 自带的默认 RSS 模板:

https://github.com/gohugoio/hugo/blob/master/tpl/tplimpl/embedded/templates/_default/rss.xml

<head>中引用RSS 订阅

​ 在您的 header.html 模板中,您可以使用 Hugo 的 输出格式<head></head> 标签中指定您的 RSS 订阅,如下所示:

1
2
3
{{ range .AlternativeOutputFormats -}}
    {{ printf `<link rel="%s" type="%s" href="%s" title="%s" />` .Rel .MediaType.Type .Permalink $.Site.Title | safeHTML }}
{{ end -}}

​ 如果您只想要 RSS 链接,则可以查询该格式:

1
2
3
{{ with .OutputFormats.Get "rss" -}}
    {{ printf `<link rel="%s" type="%s" href="%s" title="%s" />` .Rel .MediaType.Type .Permalink $.Site.Title | safeHTML }}
{{ end -}}

​ 上述两个片段中的任何一个都将为站点首页生成以下 link 标记以用于 RSS 输出:

1
<link rel="alternate" type="application/rss+xml" href="https://example.com/index.xml" title="Site Title">

在本示例中,我们假设 BaseURLhttps://example.com/$.Site.Title"Site Title"

另请参阅

20 - Sitemap模板

Sitemap Templates - Sitemap模板

https://gohugo.io/templates/sitemap-template/

​ Hugo提供了内置的站点地图(sitemap)模板。

概述

​ Hugo的内置站点地图模板符合v0.9的站点地图协议

​ 对于单语言项目,Hugo将在根目录下使用内置的sitemap.xml模板生成一个sitemap.xml文件,该文件位于publishDir中。

​ 对于多语言项目,Hugo会生成:

  • 使用内置的sitemap.xml模板,在每个站点(语言)的根目录中生成一个sitemap.xml文件。
  • 使用内置的sitemapindex.xml模板,在publishDir的根目录中生成一个sitemap.xml文件。

配置

​ 在您的站点配置中设置更改频率优先级和生成的文件名称的默认值。

config.

=== “yaml”

``` yaml
sitemap:
  changefreq: monthly
  filename: sitemap.xml
  priority: 0.5
```

=== “toml”

``` toml
[sitemap]
  changefreq = 'monthly'
  filename = 'sitemap.xml'
  priority = 0.5
```

=== “json”

``` json
{
   "sitemap": {
      "changefreq": "monthly",
      "filename": "sitemap.xml",
      "priority": 0.5
   }
}
```
  • changefreq

    页面更改频率的可能性有多大。有效的值包括alwayshourlydailyweeklymonthlyyearlynever。默认值为""(从渲染的站点地图中省略更改频率)。

  • filename

    生成的文件名称。默认值为sitemap.xml

  • priority

    相对于站点中的其他页面,该页面的优先级。有效值范围为0.0到1.0。默认值为-1(渲染站点地图时省略优先级)。

覆盖默认值

​ 在前置元数据中覆盖给定页面的默认值。

news.md

=== “yaml”

``` yaml
---
sitemap:
  changefreq: weekly
  priority: 0.8
title: News
---
```

=== “toml”

``` toml
+++
title = 'News'
[sitemap]
  changefreq = 'weekly'
  priority = 0.8
+++
```

=== “json”

``` json
{
   "sitemap": {
      "changefreq": "weekly",
      "priority": 0.8
   },
   "title": "News"
}
```

覆盖内置模板

​ 要覆盖内置的sitemap.xml模板,请在以下任一位置创建一个新文件:

  • layouts/sitemap.xml
  • layouts/_default/sitemap.xml

​ 在对页面集合进行排列时,可以使用.Sitemap.ChangeFreq.Sitemap.Priority分别访问更改频率优先级

​ 要覆盖内置的sitemapindex.xml模板,请在以下任一位置创建一个新文件:

  • layouts/sitemapindex.xml
  • layouts/_default/sitemapindex.xml

禁用Sitemap生成

​ 您可以在站点配置中禁用站点地图生成:

config.

=== “yaml”

``` yaml
disableKinds:
- sitemap
```

=== “toml”

``` toml
disableKinds = ['sitemap']
```

=== “json”

``` json
{
   "disableKinds": [
      "sitemap"
   ]
}
```

另请参阅

21 - Robots.txt

Robots.txt File - Robots.txt 文件

https://gohugo.io/templates/robots/

​ Hugo 可以像任何其他模板一样生成自定义的 robots.txt 文件。

​ 要从模板生成 robots.txt 文件,请更改站点配置

config.

=== “yaml”

``` yaml
enableRobotsTXT: true
```

=== “toml”

``` toml
enableRobotsTXT = true
```

=== “json”

``` json
{
   "enableRobotsTXT": true
}
```

​ 默认情况下,Hugo使用内置模板生成 robots.txt。

1
User-agent: *

​ 遵守Robots Exclusion Protocol的搜索引擎将把这个文件解释为允许爬取站点上的所有内容。

Robots.txt 模板查找顺序

​ 您可以使用自定义模板覆盖内置模板。Hugo使用以下查找顺序选择模板:

  1. /layouts/robots.txt
  2. /themes/<THEME>/layouts/robots.txt

Robots.txt 模板示例

layouts/robots.txt

1
2
3
4
User-agent: *
{{ range .Pages }}
Disallow: {{ .RelPermalink }}
{{ end }}

​ 该模板将为站点上的每个页面创建一个 robots.txt 文件,使用Disallow指令。遵守Robots Exclusion Protocol的搜索引擎将不会爬取站点上的任何页面。

​ 要创建一个不使用模板的 robots.txt 文件:

  1. 站点配置中将 enableRobotsTXT 设置为 false
  2. static 目录中创建一个 robots.txt 文件。

​ 请记住,Hugo在构建站点时将 static 目录 中的所有内容复制到 publishDir (通常为 public) 的根目录。

22 - 内置模板

Internal Templates - 内置模板

https://gohugo.io/templates/internal/

​ Hugo自带一组样板模板,覆盖了静态站点最常见的用例。

​ 虽然以下内置模板类似于局部模板,但它们不遵循局部模板查找顺序。

Google Analytics

​ Hugo自带内置模板支持Google Analytics,包括Google Analytics 4 (GA4)和Universal Analytics。

注意: Universal Analytics已被弃用。有关详情,请参阅Universal Analytics将被取消

Configure Google Analytics

​ 在配置文件中提供您的跟踪ID:

Google Analytics 4 (gtag.js)

config.

=== “yaml”

``` yaml
googleAnalytics: G-MEASUREMENT_ID
```

=== “toml”

``` toml
googleAnalytics = 'G-MEASUREMENT_ID'
```

=== “json”

``` json
{
   "googleAnalytics": "G-MEASUREMENT_ID"
}
```

Google Universal Analytics (analytics.js)

config.

=== “yaml”

``` yaml
googleAnalytics: UA-PROPERTY_ID
```

=== “toml”

``` toml
googleAnalytics = 'UA-PROPERTY_ID'
```

=== “json”

``` json
{
   "googleAnalytics": "UA-PROPERTY_ID"
}
```

使用Google Analytics模板

​ 然后,您可以包含Google Analytics内置模板:

1
{{ template "_internal/google_analytics_async.html" . }}

注意: 异步模板不适用于Google Analytics 4。

1
{{ template "_internal/google_analytics.html" . }}

​ 如果您想创建自己的模板,可以使用 {{ site.Config.Services.GoogleAnalytics.ID }} 访问已配置的ID。

Disqus

​ Hugo还带有用于Disqus评论的内置模板,这是一种流行的静态和动态站点评论系统。要有效地使用Disqus,您需要通过注册免费服务来获得Disqus “shortname”。

配置Disqus

​ 要使用Hugo的Disqus模板,您首先需要设置一个配置值:

config.

=== “yaml”

``` yaml
disqusShortname: your-disqus-shortname
```

=== “toml”

``` toml
disqusShortname = 'your-disqus-shortname'
```

=== “json”

``` json
{
   "disqusShortname": "your-disqus-shortname"
}
```

​ 您还可以选择在给定篇的内容的前置元数据中设置以下值:

  • disqus_identifier
  • disqus_title
  • disqus_url

使用Disqus模板

​ 要添加Disqus,请在要显示评论的模板中包含以下行:

1
{{ template "_internal/disqus.html" . }}

​ 还有一个暴露在配置中的 .Site.DisqusShortname 变量。

Disqus评论的条件加载

​ 用户已经注意到,在运行Hugo Web服务器(即通过hugo server)时启用Disqus评论会导致在关联的Disqus帐户上创建不必要的讨论。

​ 您可以创建以下 layouts/partials/disqus.html

layouts/partials/disqus.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
<div id="disqus_thread"></div>
<script type="text/javascript">

(function() {
    // Don't ever inject Disqus on localhost--it creates unwanted
    // discussions from 'localhost:1313' on your Disqus account...
    if (window.location.hostname == "localhost")
        return;

    var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
    var disqus_shortname = '{{ .Site.DisqusShortname }}';
    dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js';
    (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
})();
</script>
<noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
<a href="https://disqus.com/" class="dsq-brlink">comments powered by <span class="logo-disqus">Disqus</span></a>

​ 当您在本地主机上运行时,if 语句将跳过Disqus评论注入的初始化。

​ 然后可以按以下方式渲染自定义Disqus局部模板:

1
{{ partial "disqus.html" . }}

Open Graph

​ Hugo提供了一个内置模板用于Open Graph协议,这是一种元数据,可使页面成为社交图中的丰富对象。此格式用于Facebook和其他一些站点。

配置Open Graph

​ Hugo的Open Graph模板使用配置变量和个别页面的front-matter的混合来配置。

config.

=== “yaml”

``` yaml
params:
  description: Text about my cool site
  images:
  - site-feature-image.jpg
  title: My cool site
taxonomies:
  series: series
```

=== “toml”

``` toml
[params]
  description = 'Text about my cool site'
  images = ['site-feature-image.jpg']
  title = 'My cool site'
[taxonomies]
  series = 'series'
```

=== “json”

``` json
{
   "params": {
      "description": "Text about my cool site",
      "images": [
         "site-feature-image.jpg"
      ],
      "title": "My cool site"
   },
   "taxonomies": {
      "series": "series"
   }
}
```

content/blog/my-post.

=== “yaml”

``` yaml
audio: []
date: "2006-01-02"
description: Text about this post
images:
- post-cover.png
series: []
tags: []
title: Post title
videos: []
```

=== “toml”

``` toml
audio = []
date = '2006-01-02'
description = 'Text about this post'
images = ['post-cover.png']
series = []
tags = []
title = 'Post title'
videos = []
```

=== “json”

``` json
{
   "audio": [],
   "date": "2006-01-02",
   "description": "Text about this post",
   "images": [
      "post-cover.png"
   ],
   "series": [],
   "tags": [],
   "title": "Post title",
   "videos": []
}
```

​ Hugo使用页面标题和描述作为标题和描述元数据。从 images 数组中取前6个URL用于图像元数据。如果使用页面 bundles,并且 images 数组为空或未定义,则使用与 *feature**cover*,*thumbnail* 匹配的文件名的图像用于图像元数据。

​ 还可以设置各种可选的元数据:

  • 日期、发布日期和最后修改日期用于设置发布时间元数据(如果指定)。
  • audio and videos are URL arrays like images for the audio and video metadata tags, respectively.
  • audiovideo 是与音频和视频元数据标签对应的(与 images 类似) URL 数组。
  • 该页面上前 6 个 tags 用于标签(tags)元数据。
  • series 分类法用于将相关的 “see also"页面放入同一系列。

​ 如果使用 YouTube,这将生成一个类似于 <meta property="og:video" content="url"> 的 og:video 标签。在 YouTube 视频中使用 https://youtu.be/<id> 格式(例如:https://youtu.be/qtIqKaDlqXo)。

使用 Open Graph 模板

​ 要添加 Open Graph 元数据,请在模板的 <head> 标签之间包含以下行:

1
{{ template "_internal/opengraph.html" . }}

Twitter Cards

​ 一个内置的模板,用于为链接到您的站点的推文附加丰富的媒体的Twitter Cards元数据。

配置 Twitter Cards

​ Hugo 的 Twitter Card 模板使用一些配置变量和个别页面的 front-matter 进行混合配置。

config.

=== “yaml”

``` yaml
params:
  description: Text about my cool site
  images:
  - site-feature-image.jpg
```

=== “toml”

``` toml
[params]
  description = 'Text about my cool site'
  images = ['site-feature-image.jpg']
```

=== “json”

``` json
{
   "params": {
      "description": "Text about my cool site",
      "images": [
         "site-feature-image.jpg"
      ]
   }
}
```

content/blog/my-post.

=== “yaml”

``` yaml
description: Text about this post
images:
- post-cover.png
title: Post title
```

=== “toml”

``` toml
description = 'Text about this post'
images = ['post-cover.png']
title = 'Post title'
```

=== “json”

``` json
{
   "description": "Text about this post",
   "images": [
      "post-cover.png"
   ],
   "title": "Post title"
}
```

​ 如果页面的前置元数据中没有指定 images,则 Hugo 会搜索具有 featurecoverthumbnail 名称的 图像页面资源。如果找不到具有这些名称的图像资源,则使用在 站点配置 中定义的图像。如果根本找不到图像,则使用不带图像的 Twitter summary 卡,而不是 summary_large_image

​ Hugo 使用该页面标题和描述作为卡片的标题和描述字段。如果没有给出描述,则使用该页面摘要。

.Site.Social.twitter 变量从配置中暴露,作为 twitter:site 的值。

config.

=== “yaml”

``` yaml
social:
  twitter: GoHugoIO
```

=== “toml”

``` toml
[social]
  twitter = 'GoHugoIO'
```

=== “json”

``` json
{
   "social": {
      "twitter": "GoHugoIO"
   }
}
```

注意:@ 将会自动为您添加。

1
<meta name="twitter:site" content="@GoHugoIO"/>

使用 Twitter Cards 模板

​ 要添加 Twitter 卡片元数据,请在您的模板的 <head> 元素之后立即包含以下行:

1
{{ template "_internal/twitter_cards.html" . }}

内置模板

​ 这些模板的代码位于这里

  • _internal/disqus.html
  • _internal/google_analytics.html
  • _internal/google_analytics_async.html
  • _internal/opengraph.html
  • _internal/pagination.html
  • _internal/schema.html
  • _internal/twitter_cards.html

另请参阅

23 - 模板调试

Template Debugging - 模板调试

https://gohugo.io/templates/template-debugging/

​ 您可以使用 Go 模板的 printf 函数来调试 Hugo 模板。这些代码片段提供了一种快速简便的方式来可视化不同上下文中可用的变量。

​ 以下是一些可以添加到您的模板中以回答一些常见问题的代码片段。

​ 这些代码片段使用 Go 模板中的 printf 函数。这个函数是 Go 函数 fmt.Printf 的别名。

此上下文中有哪些变量可用?

​ 您可以使用模板语法 $. 来获取该模板的顶层上下文。这将打印出.Site下的所有值。

1
{{ printf "%#v" $.Site }}

​ 这将打印出.Permalink的值:

1
{{ printf "%#v" .Permalink }}

​ 这将打印出当前上下文(.,也称为"the dot")的所有变量的列表。

1
{{ printf "%#v" . }}

​ 在开发 homepage 时,您正在遍历的页面之一是什么样子的?

1
2
3
4
{{ range .Pages }}
    {{/* The context, ".", is now each one of the pages as it goes through the loop */}}
    {{ printf "%#v" . }}
{{ end }}

为什么我没有显示定义的变量?

​ 检查是否在 partial 函数中传递了变量:

1
{{ partial "header.html" }}

​ 这个例子将渲染 header partial,但 header partial 将没有访问任何上下文变量的权限。您需要显式地传递变量。例如,注意添加了 “the dot”

1
{{ partial "header.html" . }}

​ 点(.)被认为是理解 Hugo 模板的基础。更多信息,请参见 Introduction to Hugo Templating