跳到主要内容

模板系统

Hugo 使用 Go 模板语言来构建页面。本章将详细介绍模板语法和常用功能。

模板基础

变量

使用 {{ }} 包裹变量和表达式:

<!-- 输出标题 -->
<h1>{{ .Title }}</h1>

<!-- 输出内容 -->
<div>{{ .Content }}</div>

<!-- 输出站点标题 -->
<p>{{ .Site.Title }}</p>

点号(.)

点号表示当前上下文。在不同位置,点号代表不同的对象:

位置点号代表
单页模板Page 对象
列表模板Page 对象(列表页)
循环中当前迭代项

管道

使用 | 连接多个操作:

<!-- 转换为大写 -->
{{ .Title | upper }}

<!-- 链式操作 -->
{{ .Title | lower | truncate 20 }}

条件判断

if 语句

{{ if .Title }}
<h1>{{ .Title }}</h1>
{{ end }}

{{ if .Params.image }}
<img src="{{ .Params.image }}" alt="{{ .Title }}">
{{ end }}

if-else 语句

{{ if .Description }}
<meta name="description" content="{{ .Description }}">
{{ else }}
<meta name="description" content="{{ .Site.Params.description }}">
{{ end }}

if-else if-else 语句

{{ if eq .Section "posts" }}
<span class="badge">文章</span>
{{ else if eq .Section "tutorials" }}
<span class="badge">教程</span>
{{ else }}
<span class="badge">其他</span>
{{ end }}

条件操作符

操作符说明示例
eq等于{{ if eq .Type "posts" }}
ne不等于{{ if ne .Draft true }}
lt小于{{ if lt .Weight 10 }}
le小于等于{{ if le .Weight 10 }}
gt大于{{ if gt .WordCount 500 }}
ge大于等于{{ if ge .WordCount 500 }}
and{{ if and .Title .Content }}
or{{ if or .Params.featured .Params.pinned }}
not{{ if not .Draft }}

with 语句

with 用于检查变量是否存在并切换上下文:

{{ with .Params.author }}
<p>作者:{{ . }}</p>
{{ else }}
<p>作者:匿名</p>
{{ end }}

{{ with .Params.image }}
<img src="{{ . }}" alt="封面图">
{{ end }}

循环

range 语句

<!-- 遍历页面 -->
{{ range .Site.RegularPages }}
<article>
<h2>{{ .Title }}</h2>
<p>{{ .Summary }}</p>
</article>
{{ end }}

<!-- 遍历标签 -->
{{ range .Params.tags }}
<span class="tag">{{ . }}</span>
{{ end }}

获取索引和值

{{ range $index, $page := .Site.RegularPages }}
<div class="post post-{{ $index }}">
<h2>{{ $page.Title }}</h2>
</div>
{{ end }}

遍历 Map

{{ range $key, $value := .Site.Data.authors }}
<div class="author">
<h3>{{ $key }}</h3>
<p>{{ $value.name }}</p>
</div>
{{ end }}

循环控制

<!-- 只取前 5 个 -->
{{ range first 5 .Site.RegularPages }}
<h2>{{ .Title }}</h2>
{{ end }}

<!-- 反向遍历 -->
{{ range .Site.RegularPages.Reverse }}
<h2>{{ .Title }}</h2>
{{ end }}

<!-- 组合使用 -->
{{ range first 10 (.Site.RegularPages.ByDate.Reverse) }}
<h2>{{ .Title }}</h2>
{{ end }}

函数

Hugo 提供了丰富的内置函数。

字符串函数

<!-- 转换大小写 -->
{{ .Title | upper }} <!-- 大写 -->
{{ .Title | lower }} <!-- 小写 -->
{{ .Title | title }} <!-- 标题大小写 -->

<!-- 截断 -->
{{ .Summary | truncate 100 "..." }}

<!-- 替换 -->
{{ .Title | replace "Hugo" "HUGO" }}

<!-- 去除空白 -->
{{ .Title | trim " " }}

<!-- 分割 -->
{{ range split .Params.tags "," }}
<span>{{ . }}</span>
{{ end }}

<!-- 连接 -->
{{ delimit .Params.tags ", " }}

数学函数

{{ add 1 2 }}        <!-- 3 -->
{{ sub 5 2 }} <!-- 3 -->
{{ mul 3 4 }} <!-- 12 -->
{{ div 10 2 }} <!-- 5 -->
{{ mod 10 3 }} <!-- 1 -->

日期函数

<!-- 格式化日期 -->
{{ .Date.Format "2006-01-02" }}
{{ .Date.Format "Jan 02, 2006" }}
{{ .Date.Format "2006年01月02日" }}

<!-- 相对时间 -->
{{ .Date | time.Format ":date_long" }}
{{ .Date | time.Format ":date_short" }}
{{ .Date | time.Format ":time_long" }}

Go 的日期格式使用特定的参考时间:2006-01-02 15:04:05,每个数字代表不同的时间单位:

  • 2006:年份
  • 01:月份
  • 02:日期
  • 15:小时(24小时制)
  • 04:分钟
  • 05:秒

数组函数

<!-- 获取第一个元素 -->
{{ first 3 .Site.RegularPages }}

<!-- 获取最后一个元素 -->
{{ last 3 .Site.RegularPages }}

<!-- 判断是否包含 -->
{{ if in .Params.tags "Hugo" }}
<span>Hugo 相关</span>
{{ end }}

<!-- 判断元素是否存在 -->
{{ if intersect .Params.tags (slice "Hugo" "Web") }}
<span>相关标签</span>
{{ end }}

<!-- 创建数组 -->
{{ $tags := slice "Hugo" "Web" "Static" }}

URL 函数

<!-- 相对 URL -->
{{ .RelPermalink }}

<!-- 绝对 URL -->
{{ .Permalink }}

<!-- URL 编码 -->
{{ .Title | urlquery }}

<!-- 获取 URL 路径 -->
{{ .URL | path.Base }}

Partials(局部模板)

Partials 是可复用的模板片段,存放在 layouts/partials/ 目录。

创建 Partial

layouts/partials/header.html

<header class="site-header">
<div class="logo">
<a href="{{ .Site.BaseURL }}">{{ .Site.Title }}</a>
</div>
<nav>
{{ range .Site.Menus.main }}
<a href="{{ .URL }}">{{ .Name }}</a>
{{ end }}
</nav>
</header>

使用 Partial

<!-- 传递当前上下文 -->
{{ partial "header.html" . }}

<!-- 不传递上下文 -->
{{ partial "header.html" .Site }}

Partial 返回值

Partial 可以返回值而不是直接输出:

layouts/partials/reading-time.html

{{ $words := .WordCount }}
{{ $minutes := div $words 200 }}
{{ return $minutes }}

使用:

{{ $time := partial "reading-time.html" . }}
<p>预计阅读时间:{{ $time }} 分钟</p>

Base Template(基础模板)

Base Template 定义页面的基础结构,其他模板继承它。

定义 Base Template

layouts/_default/baseof.html

<!DOCTYPE html>
<html lang="{{ .Site.LanguageCode }}">
<head>
<meta charset="UTF-8">
<title>{{ .Title }} | {{ .Site.Title }}</title>
{{ block "head" . }}{{ end }}
</head>
<body>
{{ partial "header.html" . }}

<main>
{{ block "main" . }}{{ end }}
</main>

{{ partial "footer.html" . }}

{{ block "scripts" . }}{{ end }}
</body>
</html>

继承 Base Template

layouts/_default/single.html

{{ define "head" }}
<meta name="description" content="{{ .Description }}">
{{ end }}

{{ define "main" }}
<article>
<h1>{{ .Title }}</h1>
<time>{{ .Date.Format "2006-01-02" }}</time>
{{ .Content }}
</article>
{{ end }}

{{ define "scripts" }}
<script src="/js/post.js"></script>
{{ end }}

列表模板

列表模板用于显示内容列表。

基本列表模板

layouts/_default/list.html

{{ define "main" }}
<h1>{{ .Title }}</h1>

{{ .Content }}

<div class="posts">
{{ range .Paginator.Pages }}
<article>
<h2><a href="{{ .RelPermalink }}">{{ .Title }}</a></h2>
<time>{{ .Date.Format "2006-01-02" }}</time>
<p>{{ .Summary }}</p>
</article>
{{ end }}
</div>

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

分页

Hugo 内置分页功能:

<!-- 配置每页数量 -->
[pagination]
pagerSize = 10

<!-- 使用分页 -->
{{ $paginator := .Paginate .Pages }}
{{ range $paginator.Pages }}
<article>
<h2>{{ .Title }}</h2>
</article>
{{ end }}

<!-- 分页导航 -->
{{ template "_internal/pagination.html" . }}

<!-- 自定义分页导航 -->
<nav class="pagination">
{{ if $paginator.HasPrev }}
<a href="{{ $paginator.Prev.URL }}">上一页</a>
{{ end }}

<span>{{ $paginator.PageNumber }} / {{ $paginator.TotalPages }}</span>

{{ if $paginator.HasNext }}
<a href="{{ $paginator.Next.URL }}">下一页</a>
{{ end }}
</nav>

单页模板

单页模板用于显示单个内容页面。

layouts/_default/single.html

{{ define "main" }}
<article class="post">
<header>
<h1>{{ .Title }}</h1>
<div class="meta">
<time datetime="{{ .Date.Format "2006-01-02" }}">
{{ .Date.Format "2006年01月02日" }}
</time>
{{ with .Params.author }}
<span class="author">{{ . }}</span>
{{ end }}
</div>
{{ with .Params.tags }}
<div class="tags">
{{ range . }}
<a href="{{ "tags/" | relURL }}{{ . | urlize }}/">{{ . }}</a>
{{ end }}
</div>
{{ end }}
</header>

{{ with .Params.image }}
<img src="{{ . }}" alt="{{ $.Title }}" class="cover">
{{ end }}

<div class="content">
{{ .Content }}
</div>

<footer>
{{ with .PrevInSection }}
<a href="{{ .RelPermalink }}">上一篇:{{ .Title }}</a>
{{ end }}
{{ with .NextInSection }}
<a href="{{ .RelPermalink }}">下一篇:{{ .Title }}</a>
{{ end }}
</footer>
</article>
{{ end }}

首页模板

首页模板是网站入口页面的模板。

layouts/index.html

{{ define "main" }}
<section class="hero">
<h1>{{ .Site.Title }}</h1>
<p>{{ .Site.Params.description }}</p>
</section>

<section class="featured">
<h2>精选文章</h2>
{{ range first 5 (where .Site.RegularPages "Section" "posts") }}
<article>
<h3><a href="{{ .RelPermalink }}">{{ .Title }}</a></h3>
<p>{{ .Summary }}</p>
</article>
{{ end }}
</section>

<section class="recent">
<h2>最新文章</h2>
{{ range first 10 .Site.RegularPages }}
<article>
<h3><a href="{{ .RelPermalink }}">{{ .Title }}</a></h3>
<time>{{ .Date.Format "2006-01-02" }}</time>
</article>
{{ end }}
</section>
{{ end }}

Section 模板

Section 模板用于特定内容分区。

layouts/posts/list.html

{{ define "main" }}
<h1>{{ .Title }}</h1>
{{ .Content }}

<div class="posts">
{{ range .Pages }}
<article>
<h2><a href="{{ .RelPermalink }}">{{ .Title }}</a></h2>
<time>{{ .Date.Format "2006-01-02" }}</time>
<p>{{ .Summary }}</p>
{{ with .Params.tags }}
<div class="tags">
{{ range . }}<span>{{ . }}</span>{{ end }}
</div>
{{ end }}
</article>
{{ end }}
</div>
{{ end }}

Taxonomy 模板

Taxonomy 模板用于分类和标签页面。

layouts/_default/terms.html(所有标签/分类列表):

{{ define "main" }}
<h1>{{ .Title }}</h1>

<ul class="taxonomy-list">
{{ range .Data.Terms.Alphabetical }}
<li>
<a href="{{ .Page.RelPermalink }}">{{ .Page.Title }}</a>
<span class="count">({{ .Count }})</span>
</li>
{{ end }}
</ul>
{{ end }}

layouts/_default/taxonomy.html(特定标签/分类页面):

{{ define "main" }}
<h1>{{ .Title }}</h1>

<div class="posts">
{{ range .Pages }}
<article>
<h2><a href="{{ .RelPermalink }}">{{ .Title }}</a></h2>
<time>{{ .Date.Format "2006-01-02" }}</time>
</article>
{{ end }}
</div>
{{ end }}

模板查找顺序

Hugo 按照特定顺序查找模板,优先使用更具体的模板:

  1. layouts/<section>/single.html
  2. layouts/<section>/list.html
  3. layouts/_default/single.html
  4. layouts/_default/list.html
  5. themes/<theme>/layouts/...

小结

本章介绍了 Hugo 模板系统的核心知识:

  1. 变量、条件、循环是模板的基础
  2. Partials 用于复用模板片段
  3. Base Template 定义页面基础结构
  4. 列表模板和单页模板是最常用的模板类型
  5. Taxonomy 模板用于分类页面

下一章将学习短代码,这是在 Markdown 中插入复杂内容的方式。