Hugo 模板template Lookup order


原文链接: Hugo 模板template Lookup order

Hugo分类模板 | Hugo 27
HUGO主题结构 - yea978.github.io

首页模板: Home Page

按照以下顺序,首先检查源目录,再检查主题目录,获取模板生成页面:

layouts/demotype/index.html
layouts/demotype/home.html
layouts/demotype/list.html
layouts/demolayout.html
layouts/index.html
layouts/home.html
layouts/list.html
layouts/_default/demolayout.html
layouts/_default/index.html
layouts/_default/home.html
layouts/_default/list.html

单一内容模板:

Hugo中内容的最主要视图就是单一视图,每一个Markdown文件都会通过单一的模板来渲染。

按照以下顺序,首先检查源目录,再检查主题目录,获取模板生成页面:

/layouts/TYPE-or-SECTION/LAYOUT.html
/layouts/TYPE-or-SECTION/single.html
/layouts/_default/single.html

layouts/posts/demolayout.html 
layouts/posts/single.html 
layouts/_default/demolayout.html 
layouts/_default/single.html

内容列表模板:Section Pages

Section列表 如上面的post

按照以下顺序,首先检查源目录,再检查主题目录,获取模板生成页面:

/layouts/section/SECTION.html
/layouts/_default/section.html
/layouts/_default/list.html

layouts/posts/demolayout.html
layouts/posts/posts.html
layouts/posts/section.html
layouts/posts/list.html

layouts/section/demolayout.html
layouts/section/posts.html
layouts/section/section.html
layouts/section/list.html

layouts/_default/demolayout.html
layouts/_default/posts.html
layouts/_default/section.html
layouts/_default/list.html

Taxonomy列表:Taxonomy List Pages 如上面的hugo

按照以下顺序,首先检查源目录,再检查主题目录,获取模板生成页面:

/layouts/taxonomy/SINGULAR.html
/layouts/_default/taxonomy.html
/layouts/_default/list.html

layouts/categories/category.html
layouts/categories/taxonomy.html
layouts/categories/list.html

layouts/taxonomy/category.html
layouts/taxonomy/taxonomy.html
layouts/taxonomy/list.html

layouts/category/category.html
layouts/category/taxonomy.html
layouts/category/list.html

layouts/_default/category.html
layouts/_default/taxonomy.html
layouts/_default/list.html

分类模板:Taxonomy Terms Pages

按照以下顺序,首先检查源目录,再检查主题目录,获取模板生成页面:

/layouts/taxonomy/SINGULAR.terms.html
/layouts/_default/terms.html

layouts/categories/category.terms.html
layouts/categories/terms.html
layouts/categories/list.html

layouts/taxonomy/category.terms.html
layouts/taxonomy/terms.html
layouts/taxonomy/list.html

layouts/category/category.terms.html
layouts/category/terms.html
layouts/category/list.html

layouts/_default/category.terms.html
layouts/_default/terms.html
layouts/_default/list.html

模板变量的作用域问题

单页模板、Section 列表模板以及 Taxonomy 列表模板均可以访问网站变量和页面变量,
此外Taxonomy 列表模板可以访问代表其自身的 .Data.Singular 变量。

模板类型

模板文件混杂了 HTML 代码和模板标识符,用来设计网页布局的。Hugo 支持 Go 语言的 HTML 模板库来对网站进行布局规划,虽然模板文件本质上没有不同,可 Hugo 结合常用网站布局结构的需要将模板分为了几种角色,下面将依次介绍这些模板角色

也即页面类型 page home section taxonomy or taxonomy Term rss sitemap robotsTXT 404

首页模板

Hugo 使用首页模板(homepage template)来渲染网站首页。一般来说网站首页同其它页面具有不一样的风格,因此需要专门为其使用特定的模板进行渲染。Hugo 在生成网站时,通常会依次从下面路径中查找首页模板,将找到的第一个文件作为首页模板:

- /layouts/index.html
- /layouts/_default/list.html
- /layouts/_default/single.html

也即默认首页模板是 index.html ,当该文件不存在时,依次使用 list.htmlsingle.html 来充当首页模板。另外首页模板中可以通过模板变量 .Data.Pages 来访问网站中所有内容文档,通常我们会遍历该变量在首页创建一个文档展示列表,不过Hugo 不会对模板的创建有任何限制,如何定义首页模板完全取决于自己。

单页模板Regular Pages

Hugo 使用单页模板(single template)来渲染内容文档。换句话说,内容文档的内容将嵌入单页模板设计好的网页结构中,以此生成网页。那么当生成静态网站时,Hugo 会使用哪个单页模板来渲染内容文档呢?Hugo 会依次从下面路径列表中查找可用的单页模板,将找到的第一个单页模板文件作为当前内容文档的渲染模板:

- /layouts/`TYPE`/`LAYOUT`.html
- /layouts/`SECTION`/`LAYOUT`.html
- /layouts/`TYPE`/single.html
- /layouts/`SECTION`/single.html
- /layouts/_default/single.html

其中 TYPE 表示内容文档的类型名称,SECTION 表示内容文档的 Section ,THEME 表示主题名称,LAYOUT 表示内容文档指定的模板名。TYPELAYOUT 可分别通过内容文档头部的 type (默认跟所在 Section 同名)和 layout (默认为单页模板)进行设置 ,SECTION 则由内容文档磁盘路径对应的 Section 决定。

可以看出 Hugo 默认会先从 TYPESECTION 这些模板目录中查找文档指定的布局 LAYOUT ,再查找相应的单页模板,然后再从网站源默认的布局目录 _default 中查找单页模板,最后会查找当前主题的相关布局目录,可见 Hugo 奉行的准则是:先精确查找,再回退默认。

在单页模板中可以访问网站变量和页面变量以及模板函数,通常我们会将内容文档的内容嵌入到单页模板中,有时也许还想为模板创建一个侧变量用来显示相关信息等,怎样定义单页模板完全取决于自己。

一般情况下,当我们为网站添加过主题之后,主题都会有单页模板的,如果想要覆盖主题中定义的单页模板,可以在网站源的模板目录下面创建相应的单页模板,或者直接创建单页模板 layouts/_default/single.html 作为内容文档未找到单页模板时的默认模板。

内容视图

Hugo 使用内容视图(content views)来以不同于单页模板的方式展示内容文档。比如有时,我们只想要展示文档摘要或者文档列表项而非整个文档,内容视图在此时就特别有用了。

内容视图也是普通的模板文件,Hugo 查找内容视图时会根据当前文档的内容类型进行查找,也就是说同名的内容视图对不同内容类型渲染效果是不同的。Hugo 会依次从以下路径列表中查找可用的内容视图,将找到的第一个模板文件来作为渲染模板

- /layouts/`TYPE`/`VIEW`.html
- /layouts/_default/`VIEW`.html
- /themes/THEME/layouts/`TYPE`/`VIEW`.html
- /themes/THEME/layouts/_default/`view`.html

假定我们要为内容类型 postproject 分别创建内容视图 li.html ,则对应的模板文件路径为:/layouts/post/li.html/layouts/project/li.html 。如果我们在网站首页使用如下代码罗列所有文档

{{ range .Data.Pages }}
{{ .Render "li"}}
{{ end }}

其中 {{ .Render "li" }} 表示引用当前内容文档对应内容视图 li.htmlpostproject 使用各自的内容视图文件),在内容视图 li.html 中可以访问任何页面变量,下面是 li.html 示例

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

列表模板

Hugo 使用列表模板(list template)渲染多个被罗列的内容文档,比如:分类标签页面和 Section 页面通常需要罗列逻辑上从属于该类别的所有文档。值得注意的是,不同于单页文档总是被内容文档填充,列表模板一般却不会被内容文档填充(下文会介绍什么情况下列表模板也会填充内容文档)。

Hugo 中列表模板常见的应用场景有:Section 列表页、Taxonomy 列表页、Section RSS 以及 Taxonomy RSS等(注:网站首页虽然也是列表页,可因其特殊性,需要使用特定的模板渲染)。这些页面渲染后的 URL 路径分别如下

  • Section 列表页

baseURL/SECTION/ ,例如:http://1.com/post/

  • Taxonomy 列表页

baseURL/PLURAL/TERM/ ,例如:http://1.com/tags/python/

  • Section RSS

baseURL/SECTION/index.html ,例如:http://1.com/post/index.html

  • Taxonomy RSS

baseURL/PLURAL/TERM/index.html ,例如:http://1.com/tags/python/

此外,Hugo 会依次从路径列表中查找可用的列表模板,将找到的第一个列表模板文件来作为渲染模板。以上介绍的常见列表页面的查找路径如下

- Section 列表
  - /layouts/section/`SECTION`.html
  - /layouts/_default/section.html
  - /layouts/_default/list.html
  - /themes/THEME/layouts/section/`SECTION`.html
  - /themes/THEME/layouts/_default/section.html
  - /themes/THEME/layouts/_default/list.html
- Taxonomy 列表
  - /layouts/taxonomy/`SINGULAR`.html
  - /layouts/_default/taxonomy.html
  - /layouts/_default/list.html
  - /themes/THEME/layouts/taxonomy/`SINGULAR`.html
  - /themes/THEME/layouts/_default/taxonomy.html
  - /themes/THEME/layouts/_default/list.html
- Section RSS
  - /layouts/section/`SECTION`.rss.xml
  - /layouts/_default/rss.xml
  - /themes/THEME/layouts/section/`SECTION`.rss.xml
  - /themes/THEME/layouts/_default/rss.xml
- Taxonomy RSS
  - /layouts/taxonomy/`SINGULAR`.rss.xml
  - /layouts/_default/rss.xml
  - /themes/THEME/layouts/taxonomy/`SINGULAR`.rss.xml
  - /themes/THEME/layouts/_default/rss.xml

从上面模板的查找路径可以看出,Hugo 首先会查找为特定 SECTIONTAXONOMY 定义的模板文件,如果查找失败,会再查找 Section 和 Taxonomy 通用的模板文件,如果还是找不到就使用 layouts/_defaults/list.htmllayouts/_defaults/rss.xml

既然知道了列表模板的用途,也知道了模板文件的查找路径,那么列表模板文件中该写些什么呢?列表文件也是一个普通的模板文件,在模板中可以使用任何 Go 内置模板函数,还可以访问网站模板变量和页面模板变量(用于 Taxonomy 的模板还可以访问代表当前分类的变量 .Data.Singular )。根据列表模板的用途一般来说会在模板中为内容文档创建一个展示列表,此外也许希望对这个内容文档分类或者剔除某些文档,利用简洁而强大的 Go 模板方法可以自定义任何复杂的列表页面。下面是一个用于 Section 的列表模板示例

{{ partial "header.html" . }}
{{ partial "subheader.html" . }}

<section id="main">
  <div>
   <h1 id="title">{{ .Title }}</h1>
        <ul id="list">
            {{ range .Data.Pages }}
                {{ .Render "li"}}
            {{ end }}
        </ul>
  </div>
</section>

{{ partial "footer.html" . }}

分类模板

Hugo 使用分类模板(taxonomy terms template)来渲染当前分类下的所有标签。

要注意同Taxonomy 列表页相区分,Taxonomy 列表页用来罗列属于某个标签下所有的内容文档,优先查找模/layouts/taxonomy/SINGULAR.html 作为该标签列表页的模板,且将页面渲染于 baseURL/PLURAL/TERM/ 。而分类模板页面是用来罗列当前分类下所有标签的,优先查找 /layouts/taxonomy/SINGULAR.terms.html 作为页面模板,且渲染于 baseURL/PLURAL/

Hugo 会依次从路径列表中查找可用的模板,将找到的第一个模板文件来作为渲染模板

  • /layouts/taxonomy/SINGULAR.terms.html
  • /layouts/_default/terms.html
  • /themes/THEME/layouts/taxonomy/SINGULAR.terms.html
  • /themes/THEME/layouts/_default/terms.html

如果以上模板都不存在,Hugo 就不会渲染分类标签页面。换句话说,分类标签页面的渲染也不一定必须单独使用一个模板文件,我们可以在页面侧边栏之类的地方来渲染分类标签(比如:侧边栏实现一个标签云)。

分类模板中除了可以访问网站变量和页面变量外,还有一些关于分类标签的变量可供我们使用:

.Data.Singular						分类的单数名称,比如:tag
.Data.Plural						分类的复数名称,比如:tags
.Data.Pages							属于当前分类的所有页面
.Data.Terms							属于当前分类的所有标签
.Data.Terms.Alphabetical			属于当前分类的所有标签(字母序)
.Data.Terms.ByCount					属于当前分类的所有标签(根据标签下文档数量排序)

下面是一个示例分类模板,该模板罗列出了当前分类下的所有标签,并给出了标签下所有文档的链接

{{ partial "header.html" . }}
{{ partial "subheader.html" . }}

<section id="main">
  <div>
    <h1 id="title">{{ .Title }}</h1>

    {{ $data := .Data }}
    {{ range $key,$value := .Data.Terms.ByCount }}
    <h2><a href="{{ .Site.LanguagePrefix }}/{{ $data.Plural }}/{{ $value.Name | urlize }}">{{ $value.Name }}</a> {{ $value.Count }}</h2>
    <ul>
    {{ range $value.Pages.ByDate }}
      <li><a href="{{ .Permalink }}">{{ .Title }}</a></li>
    {{ end }}
    </ul>
    {{ end }}
  </div>
</section>

{{ partial "footer.html" . }}

模板引用

partial: 默认的引用方式

`{{ partial "meta.html" . }}`

template: 仅用于引用内部的模板

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

Hugo 使用片段模板(partial template)作为其它模板文件的原材料,比如首页模板、单页模板、列表模板等这些模板通常会使用片段模板来创建。这里之所以将片段模板比作原材料,是因为片段模板通常包含了其它模板中的公共部分,反过来说,我们应该将多个模板中的公共内容分离出来创建片段模板文件,然后可以在其它模板中引用该片段文件。使用片段模板的好处在于,不需要重复定义相同的模板内容,而且片段模板十分有利于主题资源的开发,主题中应该将那些想要让用户覆盖的模板内容单独作为一个片段模板,这样主题的使用者只需要定义相同的片段模板就可以对主题片段模板进行替换,片段模板文件是比普通模板文件更加细粒度的模板内容容器。

如何创建片段模板呢?Hugo 默认将模板目录 /layouts/partials/ 及其子目录中的模板文件看作片段模板,片段模板的内容如同普通模板一样可以访问各种模板变量和模板函数,不过片段模板可以访问到的模板变量取决于引用该模板时传入了怎样的变量进来(后面会有讲,如何引用片段模板以及如何传递变量到片段模板)。在网站中最为常见的片段模板也许就是网页头和网页脚,因为网页头和网页脚在网站大多数页面中都是相同的,将其分离于片段模板中是明智的选择,假设我们创建了 /layouts/partials/header.html/layouts/partials/footer.html 片段模板文件,它们的内容分别为

<!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>
<body lang="en">

<footer>
  <div>
    <p>
    &copy; 2013-14 Steve Francia.
    <a href="http://creativecommons.org/licenses/by/3.0/" title="Creative Commons Attribution">Some rights reserved</a>;
    please attribute properly and link back. Hosted by <a href="http://servergrove.com">ServerGrove</a>.
    </p>
  </div>
</footer>
<script type="text/javascript">

  var _gaq = _gaq || [];
  _gaq.push(['_setAccount', 'UA-XYSYXYSY-X']);
  _gaq.push(['_trackPageview']);

  (function() {
    var ga = document.createElement('script');
    ga.src = ('https:' == document.location.protocol ? 'https://ssl' :
        'http://www') + '.google-analytics.com/ga.js';
    ga.setAttribute('async', 'true');
    document.documentElement.firstChild.appendChild(ga);
  })();

</script>
</body>
</html>

以上模板内容除了常规的 HTML 代码外,还出现了像 {{ partial "meta.html" . }} 这样的模板语句,这条语句在这里的作用是引用片段模板 meta.html 到当前模板文件中(即 header.html 片段模板文件),就是说 Hugo 允许我们在片段模板中再次引用片段模板。

下面让我们研究一下,如何引用一个片段模板文件,引用片段模板的语法为:{{ partial "path/to/file.html" variables }} ,其中 path/to/file.html 表示被引用的片段模板文件相对于 /layouts/partials/ 目录的路径,比如想要引用 /layouts/partials/post/sidebar.html ,则对应的引用路径为 post/sidebar.html 。其中 variables 表示要传入片段模板的变量(片段模板除了这些传入的变量,是无法访问其它变量的),通常我们会将代表当前模板内所有变量的 . 作为 variables 传入片段模板中。

有没有想过,很多模板引用相同的片段模板文件,在生成网页时,这些片段模板是不是在每个引用模板中都要重新渲染一次呢?有没有办法减少片段模板的渲染次数,毕竟片段模板生成的网页片段除了根据传入变量不同会有改变外,基本的网页结构是相似的。如果想要让 Hugo 提升片段模板的渲染效率(Hugo 会自动缓存已经渲染好的片段模板供后续使用),可以在引用模板文件时用 partialCached 来代替 partial ,并且 Hugo 还支持用户按照类别缓存片段模板,比如: {{ partialCached "footer.html" . .Section }} 的意思是,为每个 Section 渲染一次 footer.html 模板。

模板调试

模板编写中错误在所难免,可以使用模板函数 printf 调试模板变量,下面是几个常见调试样例

{{ printf "%#v" . }}
{{ printf "%#v" $.Site }}
{{ printf "%#v" .Permalink }}
{{ range .Data.Pages }}
    {{/* The context, ".", is now a Page */}}
    {{ printf "%#v" . }}
{{ end }}
`