hugo开发主题2-模板

警告
本文最后更新于 2020-11-11,文中内容可能已过时。

Hugo 用到最后,终究还是免不了走上自己修改甚至开发主题的道路,本篇介绍 Hugo 的核心之一:模板。

1. 基本介绍

模板指的是 HTML 的模板,是将 HTML 中的一些内容抽象为变量,在页面渲染时才进行替换的一种方法,如果学过 Go,会比较容易理解,因为 Hugo 的模板正式以 Go 的 html/templatetext/template 两个库为基础的。

简而言之,Hugo 中提到模板就是提到 HTML,写模板就是写 HTML。

Hugo 主题中 HTML 的入口文件为 _default/baseof.html,这是所有页面渲染时的默认模板,除非你指定了在查找顺序上具有更高优先级的 baseof.html 文件。

注:查找顺序将在第 2 节介绍。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!-- layouts/_default/baseof.html -->

<!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>

上面的代码与普通 HTML 代码的区别就是以双大括号 {{ }} 包围的区域,这就是模板的写法。以 {{ .Site.Title }} 为例,在渲染时会用主题配置文件 config.toml 中定义的 title 字段替换,是不是就像被抽象而出的变量?

{{ block "title" }} 是一种更大的抽象范围:代码块,我们会在其它的 HTML 文件中定义这段代码块,然后在渲染时替换这一部分内容,比如,我们在 layouts/_default/list.html 中定义了 titlemain 代码块如下

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

渲染时会替换掉 baseof.html 文件中如下内容,注意,额外定义的这部分代码要以 {{ block “title”}} 开始,指明替换哪一段 block,所有的模板语法代码段都要以 {{ end }} 结束。

1
2
3
4
{{ block "title" . }}
    <!-- Blocks may include default content. -->
    {{ .Site.Title }}
{{ end }}

实际的操作是 list.htmlbaseof.html 继承所有代码,然后用自己定义的 titlemain 代码段替换相关内容,是在 list.html 中操作的,不是在 baseof.html 中操作的。

2. 页面查找顺序

Hugo 主题所有的 HTML 文件都位于主题目录下 layouts 目录中,我们所看到的博客的每个页面的渲染,都要首先从这里选择一个 HTML 文件作为模板,页面查找顺序指的是渲染页面时在 layouts 目录下的查找顺序。比如,Home Page 首先在 layouts 根目录下查找 index.html 文件,如果找不到,查找 home.html 文件或 list.html 文件,如果依然找不到,在 layouts/_default 目录下查找这三个文件。

与查找顺序相关的,除了上例提到的文件夹的不同,还和页面类型(Kind)、输出格式(Output Format)、语言(Language)等有关,我们主要关注的是页面类型 Kind

页面类型有两种:single page 和 list page。前者用于单个页面渲染,后者用于在单个页面中渲染多个内容,举个例子,一个归档页面通常包含数十篇文章,这就属于 list page。其中,regular page 属于前者,section page、home page、taxonomy list page、taxonomy term page 属于后者。下面进行一一介绍。

注:home page 是个例外,它虽然属于 list page,但有自己的专用模板。

home page

网站首页通常和其它页面不同,因此有自己的专用模板,而且当网站只有一个页面时,home page 是唯一需要的文件。

home page 可以读取 config.toml 中定义的网站全局变量,也可以读取 content/_index.md 中的元数据(通常以 yaml/toml 格式定义在开头,称为 front matter)。

一些重要的文件查找顺序如下,其中 Home page 是主页面,Base template for home page 是主页面继承的模板,RSS home 是主页面的 RSS 文件。

ExampleOutputFormatSuffixTemplate Lookup Order
Home pageHTMLhtmllayouts/index.html
layouts/home.html
layouts/list.html
layouts/_default/index.html
layouts/_default/home.html
layouts/_default/list.html
Base template for home pageHTMLhtmllayouts/index-baseof.html
layouts/home-baseof.html
layouts/list-baseof.html
layouts/baseof.html
layouts/_default/index-baseof.html
layouts/_default/home-baseof.html
layouts/_default/list-baseof.html
layouts/_default/baseof.html
RSS homeRSSxmllayouts/index.xml
layouts/home.xml
layouts/list.xml
layouts/_default/index.xml
layouts/_default/home.xml
layouts/_default/list.xml

我们重点介绍一下 Base template for home page,在 Home page 中,定义的是一段代码块,正如我们在第一节中所述,它需要先继承一个 HTML 文件的结构,然后替换其中使用 {{ block "name" }} 定义的部分,这里所继承的文件就是 Base template for home page。下面是一个例子

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
<!-- layouts/index.html -->
{{ 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 }}

section page

section 是一组页面的集合,默认的,content 目录下每个一级目录都是一个 section,更深层次的目录,如果想成为 section,需要建立 _index.md 文件。一个例子如下

1
2
3
4
5
6
7
8
content
└── blog        <-- Section, because first-level dir under content/
    ├── funny-cats
    │   ├── mypost.md
    │   └── kittens         <-- Section, because contains _index.md
    │       └── _index.md
    └── tech                <-- Section, because contains _index.md
        └── _index.md

section page 就是用来渲染 section 的模板,可以访问 section 中的所有内容和 front matter,其查找顺序如下

ExampleOutputFormatSuffixTemplate Lookup Order
Section list for “posts” sectionHTMLhtmllayouts/posts/posts.html
layouts/posts/section.html
layouts/posts/list.html
layouts/section/posts.html
layouts/section/section.html
layouts/section/list.html
layouts/_default/posts.html
layouts/_default/section.html
layouts/_default/list.html

下面是一个 section page 的例子

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
<!-- layouts/_default/section.html -->
{{ 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 函数可以用来获取一组给定类型的页面,假设 content 目录结构如下

 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

输出 blog section 的 title 可以使用如下语句

1
2
3
<h1>{{ with .Site.GetPage "section" "blog" }}{{ .Title }}{{ end }}</h1>
<!-- 输出如下 -->
<h1>My Hugo Blog</h1>

如果是 event section,因为没有 _index.md 文件,会输出文件名

1
2
3
<h1>{{ with .Site.GetPage "section" "events" }}{{ .Title }}{{ end }}</h1>
<!-- 输出如下 -->
<h1>Events</h1>

taxonomy page

包括 taxonomy list pages 和 taxonomy terms pages,用于用户自定义的内容分组,比如分类和标签页面,展示了文章间的一种逻辑关系。下面解释几个术语

  • Taxonomy:对内容进行分类的字段
  • Term:分类的键
  • Value:分类的值,就是内容

举个例子,演员 是 Taxonomy,克里斯·埃文斯 是 Term,《美国队长》 是 Value,如果按 Taxonomy 组织页面,就是 taxonomy list pages,如果按 Term 组织页面,就是 taxonomy terms pages,如果是显示单个内容,那么使用 single page。页面的查找顺序如下

ExampleOutputFormatSuffixTemplate Lookup Order
Taxonomy list in categoriesHTMLhtmllayouts/categories/terms.html
layouts/categories/taxonomy.html
layouts/categories/list.html
layouts/category/terms.html
layouts/category/taxonomy.html
layouts/category/list.html
layouts/taxonomy/terms.html
layouts/taxonomy/taxonomy.html
layouts/taxonomy/list.html
layouts/_default/terms.html
layouts/_default/taxonomy.html
layouts/_default/list.html
Taxonomy term in categoriesHTMLhtmllayouts/categories/term.html
layouts/categories/category.html
layouts/categories/taxonomy.html
layouts/categories/list.html
layouts/term/term.html
layouts/term/category.html
layouts/term/taxonomy.html
layouts/term/list.html
layouts/taxonomy/term.html
layouts/taxonomy/category.html
layouts/taxonomy/taxonomy.html
layouts/taxonomy/list.html
layouts/category/term.html
layouts/category/category.html
layouts/category/taxonomy.html
layouts/category/list.html
layouts/_default/term.html
layouts/_default/category.html
layouts/_default/taxonomy.html
layouts/_default/list.html

single page

single page 是 Hugo 最主要的一个模板,用来渲染每一篇 Markdown 文章。页面的查找顺序如下

ExampleOutputFormatSuffixTemplate Lookup Order
Single page in “posts” sectionHTMLhtmllayouts/posts/single.html
layouts/_default/single.html
Base template for single page in “posts” sectionHTMLhtmllayouts/posts/single-baseof.html
layouts/posts/baseof.html
layouts/_default/single-baseof.html
layouts/_default/baseof.html

一个 single page 的示例如下

 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
<!-- layouts/posts/single.html -->
{{ 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 .Params.topics }}
    <ul id="topics">
      {{ range . }}
        <li><a href="{{ "topics" | absURL}}{{ . | urlize }}">{{ . }}</a> </li>
      {{ end }}
    </ul>
    {{ end }}
    {{ with .Params.tags }}
    <ul id="tags">
      {{ range . }}
        <li> <a href="{{ "tags" | absURL }}{{ . | urlize }}">{{ . }}</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 }}
支付宝
微信
0%