Hugo 静态网站构建实战手册

模板系统

理解 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/*.htmlMarkdown 中调用的组件
输出模板layouts/_default/rss.xmlsearch.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相对链接
.ParamsFront Matter 中的自定义参数
.ResourcesPage 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

实际查找顺序更细,涉及 typekindoutput 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 排查缓存错觉

下一步

继续阅读 主题开发,学习如何安全地定制或创建主题。

评论

0%