模板系统
理解 Hugo 模板查找顺序、基础模板、区块、局部模板、短代码和常用模板函数
模板系统
Hugo 模板决定内容如何变成页面。写文章时你只接触 Markdown;当你想调整列表页、文章页、导航、SEO、卡片、归档或搜索数据时,就需要理解模板系统。
Hugo 使用 Go template 语法,模板文件通常放在 layouts/ 中。主题里的模板放在 themes/主题名/layouts/ 中,站点根目录的 layouts/ 优先级更高。
模板类型
| 模板 | 常见位置 | 作用 |
|---|---|---|
| 基础模板 | layouts/_default/baseof.html | 定义 HTML 骨架 |
| 单页模板 | layouts/_default/single.html | 渲染文章、页面 |
| 列表模板 | layouts/_default/list.html | 渲染栏目、分类列表 |
| 首页模板 | layouts/index.html | 渲染首页 |
| 局部模板 | layouts/partials/*.html | 可复用组件 |
| 短代码 | layouts/shortcodes/*.html | Markdown 中调用的组件 |
| 输出模板 | layouts/_default/rss.xml、search.json | 生成 RSS、JSON 等 |
最小模板结构
layouts/
├── _default/
│ ├── baseof.html
│ ├── list.html
│ └── single.html
├── partials/
│ ├── head.html
│ ├── header.html
│ └── footer.html
└── index.html
基础模板 baseof.html
baseof.html 是页面骨架,其他模板通过 block 填充内容。
<!doctype html>
<html lang="{{ site.Language.LanguageCode | default "zh-CN" }}">
<head>
{{ partial "head.html" . }}
</head>
<body>
{{ partial "header.html" . }}
<main>
{{ block "main" . }}{{ end }}
</main>
{{ partial "footer.html" . }}
</body>
</html>
block "main" 是可被子模板覆盖的区域。
单页模板 single.html
{{ define "main" }}
<article class="post">
<header>
<h1>{{ .Title }}</h1>
{{ with .Description }}
<p>{{ . }}</p>
{{ end }}
<time datetime="{{ .Date.Format "2006-01-02" }}">
{{ .Date.Format "2006-01-02" }}
</time>
</header>
<div class="content">
{{ .Content }}
</div>
</article>
{{ end }}
常用页面变量:
| 变量 | 说明 |
|---|---|
.Title | 页面标题 |
.Description | 页面描述 |
.Date | 发布日期 |
.Lastmod | 最后修改时间 |
.Content | 渲染后的正文 |
.Summary | 摘要 |
.RelPermalink | 相对链接 |
.Params | Front Matter 中的自定义参数 |
.Resources | Page Bundle 资源 |
列表模板 list.html
{{ define "main" }}
<section class="section">
<h1>{{ .Title }}</h1>
{{ with .Content }}
<div class="section-intro">{{ . }}</div>
{{ end }}
{{ $paginator := .Paginate .Pages }}
{{ range $paginator.Pages }}
<article>
<h2><a href="{{ .RelPermalink }}">{{ .Title }}</a></h2>
<p>{{ .Summary }}</p>
</article>
{{ end }}
{{ template "_internal/pagination.html" . }}
</section>
{{ end }}
文档类栏目常用权重排序:
{{ range .Pages.ByWeight }}
<a href="{{ .RelPermalink }}">{{ .Title }}</a>
{{ end }}
博客列表常用日期排序:
{{ range .Pages.ByDate.Reverse }}
<a href="{{ .RelPermalink }}">{{ .Title }}</a>
{{ end }}
首页模板 index.html
{{ define "main" }}
<section class="home">
<h1>{{ site.Title }}</h1>
<p>{{ site.Params.description }}</p>
</section>
<section class="latest-posts">
<h2>最新文章</h2>
{{ range first 6 (where site.RegularPages "Section" "posts") }}
<article>
<h3><a href="{{ .RelPermalink }}">{{ .Title }}</a></h3>
<p>{{ .Summary }}</p>
</article>
{{ end }}
</section>
{{ end }}
常见集合:
{{ site.RegularPages }}
{{ site.Sections }}
{{ where site.RegularPages "Section" "posts" }}
{{ first 10 site.RegularPages }}
模板查找顺序
Hugo 会根据页面类型、Section、Layout、Kind 等信息寻找最匹配的模板。
例如:
content/posts/hello.md
常见查找顺序可以理解为:
layouts/posts/single.html
layouts/_default/single.html
themes/主题名/layouts/posts/single.html
themes/主题名/layouts/_default/single.html
如果 Front Matter 指定:
layout: "featured"
Hugo 会优先寻找类似:
layouts/posts/featured.html
layouts/_default/featured.html
实际查找顺序更细,涉及 type、kind、output format 等,但日常改主题先记住“更具体优先,站点覆盖主题”即可。
局部模板 partials
局部模板用于复用组件。
layouts/partials/
├── head.html
├── header.html
├── footer.html
├── post-card.html
└── breadcrumbs.html
调用:
{{ partial "post-card.html" . }}
传入自定义字典:
{{ partial "post-card.html" (dict "page" . "showSummary" true) }}
局部模板中读取:
{{ $page := .page }}
{{ $showSummary := .showSummary }}
<article>
<h2><a href="{{ $page.RelPermalink }}">{{ $page.Title }}</a></h2>
{{ if $showSummary }}<p>{{ $page.Summary }}</p>{{ end }}
</article>
partialCached
不依赖当前页面变化的组件可以缓存:
{{ partialCached "footer.html" . }}
如果组件依赖语言或配置,可以加缓存键:
{{ partialCached "sidebar.html" . .Section site.Language.Lang }}
缓存能提升大型站点构建速度,但不要缓存依赖当前页面状态又没有设置缓存键的组件。
短代码 shortcodes
短代码让 Markdown 可以调用模板组件。
layouts/shortcodes/notice.html
{{ $type := .Get "type" | default "info" }}
<aside class="notice notice-{{ $type }}">
{{ .Inner | markdownify }}
</aside>
Markdown 中使用:
{{< notice type="warning" >}}
部署前请确认 `baseURL` 已改为线上域名。
{{< /notice >}}
短代码适合提示框、视频、按钮、图库、代码演示等重复结构。
常用条件判断
{{ if .Params.featured }}
<span>精选</span>
{{ else }}
<span>普通文章</span>
{{ end }}
{{ with .Params.cover }}
<img src="{{ . }}" alt="">
{{ end }}
with 只有在值存在且非空时执行,并把 . 临时切换为该值。
range 循环
{{ range .Params.tags }}
<span>{{ . }}</span>
{{ end }}
循环文章:
{{ range where site.RegularPages "Section" "posts" }}
<a href="{{ .RelPermalink }}">{{ .Title }}</a>
{{ else }}
<p>暂无文章。</p>
{{ end }}
字典与变量
{{ $posts := where site.RegularPages "Section" "posts" }}
{{ $latest := first 5 $posts }}
{{ range $latest }}
<a href="{{ .RelPermalink }}">{{ .Title }}</a>
{{ end }}
自定义字典:
{{ $meta := dict "title" .Title "url" .Permalink }}
{{ partial "share.html" $meta }}
常用函数
| 函数 | 用途 | 示例 |
|---|---|---|
where | 过滤集合 | where site.RegularPages "Section" "posts" |
first | 取前 N 个 | first 5 $posts |
sort | 排序 | sort .Pages "Date" "desc" |
default | 默认值 | `.Params.cover |
printf | 格式化字符串 | printf "%s - %s" .Title site.Title |
dict | 创建字典 | dict "page" . "active" true |
slice | 创建数组 | slice "posts" "notes" |
time.Format | 格式化日期 | time.Format "2006-01-02" .Date |
relURL | 转相对 URL | `“css/main.css” |
absURL | 转绝对 URL | `“images/logo.png” |
SEO head 示例
<title>{{ if .IsHome }}{{ site.Title }}{{ else }}{{ .Title }} - {{ site.Title }}{{ end }}</title>
<meta name="description" content="{{ .Description | default site.Params.description }}">
<link rel="canonical" href="{{ .Permalink }}">
{{ with .OutputFormats.Get "RSS" }}
<link rel="alternate" type="application/rss+xml" href="{{ .RelPermalink }}" title="{{ site.Title }}">
{{ end }}
社交分享:
<meta property="og:title" content="{{ .Title }}">
<meta property="og:description" content="{{ .Description | default .Summary }}">
<meta property="og:url" content="{{ .Permalink }}">
<meta property="og:type" content="{{ if .IsPage }}article{{ else }}website{{ end }}">
菜单模板
<nav>
{{ range site.Menus.main }}
<a href="{{ .URL }}" {{ if $.IsMenuCurrent "main" . }}aria-current="page"{{ end }}>
{{ .Name }}
</a>
{{ end }}
</nav>
如果用 pageRef 配置菜单,Hugo 会更好地处理语言和相对路径。
面包屑模板
{{ with .Ancestors.Reverse }}
<nav aria-label="breadcrumb">
<a href="{{ site.Home.RelPermalink }}">首页</a>
{{ range . }}
<span>/</span>
<a href="{{ .RelPermalink }}">{{ .Title }}</a>
{{ end }}
</nav>
{{ end }}
调试模板
开发时可以输出变量:
<pre>{{ debug.Dump .Params }}</pre>
或临时打印:
{{ warnf "Current page: %s" .File.Path }}
构建时查看:
hugo --templateMetrics
hugo --templateMetricsHints
这些命令可以帮助发现慢模板和可缓存的 partial。
模板开发建议
- 先覆盖站点
layouts/,不要直接改第三方主题 - 公共组件放到
partials/ - Markdown 中重复结构用
shortcodes/ - 列表页和单页分开处理
- 能用配置解决的,不写死在模板里
- 复杂集合先存变量,提升可读性
- 修改模板后用
hugo server --disableFastRender排查缓存错觉
下一步
继续阅读 主题开发,学习如何安全地定制或创建主题。
评论