模板系统
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 按照特定顺序查找模板,优先使用更具体的模板:
layouts/<section>/single.htmllayouts/<section>/list.htmllayouts/_default/single.htmllayouts/_default/list.htmlthemes/<theme>/layouts/...
小结
本章介绍了 Hugo 模板系统的核心知识:
- 变量、条件、循环是模板的基础
- Partials 用于复用模板片段
- Base Template 定义页面基础结构
- 列表模板和单页模板是最常用的模板类型
- Taxonomy 模板用于分类页面
下一章将学习短代码,这是在 Markdown 中插入复杂内容的方式。