内容管理
内容是 Hugo 站点的核心。本章将详细介绍如何创建、组织和管理 Hugo 内容。
Front Matter
Front Matter 是内容文件开头的元数据部分,用于定义页面的各种属性。Hugo 支持三种格式。
TOML 格式(推荐)
使用 +++ 分隔:
+++
title = "我的文章"
date = 2024-01-15T10:00:00+08:00
draft = false
categories = ["技术"]
tags = ["Hugo", "静态网站"]
+++
文章内容...
YAML 格式
使用 --- 分隔:
---
title: "我的文章"
date: 2024-01-15T10:00:00+08:00
draft: false
categories:
- 技术
tags:
- Hugo
- 静态网站
---
文章内容...
JSON 格式
使用 {} 包裹:
{
"title": "我的文章",
"date": "2024-01-15T10:00:00+08:00",
"draft": false,
"categories": ["技术"],
"tags": ["Hugo", "静态网站"]
}
文章内容...
常用 Front Matter 变量
| 变量 | 说明 | 示例 |
|---|---|---|
title | 页面标题 | "我的文章" |
date | 发布日期 | 2024-01-15 |
lastmod | 最后修改日期 | 2024-01-20 |
draft | 是否为草稿 | true / false |
publishDate | 发布日期(用于定时发布) | 2024-02-01 |
expiryDate | 过期日期 | 2025-01-01 |
description | 页面描述 | "文章简介" |
summary | 自定义摘要 | "这是摘要" |
slug | URL 别名 | "my-post" |
url | 完整 URL | /custom/path/ |
aliases | 重定向别名 | ["/old-url/"] |
weight | 排序权重 | 10 |
tags | 标签 | ["Hugo", "Web"] |
categories | 分类 | ["技术"] |
keywords | 关键词 | ["静态网站"] |
author | 作者 | "张三" |
image | 特色图片 | "/images/cover.jpg" |
toc | 是否显示目录 | true / false |
comment | 是否允许评论 | true / false |
自定义 Front Matter
你可以添加任意自定义变量:
+++
title = "产品评测"
rating = 5
product_name = "Hugo"
pros = ["快速", "简单"]
cons = ["学习曲线"]
+++
评分:{{ .Params.rating }}/5
在模板中通过 .Params 访问:
<p>评分:{{ .Params.rating }}/5</p>
内容组织
Hugo 提供了多种方式来组织内容。
Section(分区)
Section 是基于 content 目录结构自动创建的内容分组。
content/
├── posts/ # posts section
│ ├── _index.md
│ └── article.md
├── tutorials/ # tutorials section
│ ├── _index.md
│ └── guide.md
└── about.md # 根级别页面
每个 Section 可以有自己的列表模板和单页模板。
Taxonomy(分类法)
Taxonomy 是用户定义的内容分类系统,默认支持 tags 和 categories。
配置分类法
# hugo.toml
[taxonomies]
tag = "tags"
category = "categories"
author = "authors"
使用分类法
+++
title = "Hugo 教程"
tags = ["Hugo", "教程"]
categories = ["技术文档"]
authors = ["张三", "李四"]
+++
访问分类法页面
- 所有标签:
/tags/ - 特定标签:
/tags/hugo/ - 所有分类:
/categories/ - 特定分类:
/categories/技术文档/
Page Bundle(页面捆绑)
Page Bundle 将页面及其相关资源组织在一起。
Leaf Bundle(叶子捆绑)
以 index.md 结尾,有自己的 URL:
content/
└── posts/
└── my-post/
├── index.md
├── cover.jpg
└── images/
└── photo.jpg
在 Markdown 中引用资源:


Branch Bundle(分支捆绑)
以 _index.md 结尾,用于列表页:
content/
└── posts/
├── _index.md
├── post-1.md
└── post-2.md
页面资源元数据管理
页面资源的元数据可以通过页面的 Front Matter 中的 resources 参数来管理。这允许你为资源文件设置名称、标题和自定义参数,在模板中方便地引用和处理资源。
基本用法
为特定资源设置元数据:
---
date: "2024-01-25"
title: "应用文档"
resources:
- name: header
src: images/sunset.jpg
- name: guide-pdf
src: documents/guide.pdf
title: 使用指南
- name: checklist-pdf
src: documents/checklist.pdf
title: 检查清单
---
TOML 格式:
+++
date = '2024-01-25'
title = '应用文档'
[[resources]]
name = 'header'
src = 'images/sunset.jpg'
[[resources]]
name = 'guide-pdf'
src = 'documents/guide.pdf'
title = '使用指南'
[[resources]]
name = 'checklist-pdf'
src = 'documents/checklist.pdf'
title = '检查清单'
+++
资源属性说明
| 属性 | 说明 |
|---|---|
name | 资源名称,用于 Match、Get 和 GetMatch 方法匹配 |
src | 资源文件路径,支持通配符 |
title | 资源标题 |
params | 自定义参数映射 |
通配符批量设置
使用通配符可以批量为多个资源设置属性:
resources:
# 所有 PDF 文件
- src: '**.pdf'
params:
icon: pdf
# 所有 Word 文档
- src: '**.docx'
params:
icon: word
# images 目录下的所有图片
- src: 'images/*.jpg'
params:
type: photo
:counter 占位符
:counter 是一个特殊的占位符,用于在 name 和 title 参数中自动生成序号:
resources:
- src: '*specs.pdf'
title: '规格说明 #:counter'
- name: 'pdf-file-:counter'
src: '**.pdf'
假设页面包含 photo_specs.pdf、other_specs.pdf、guide.pdf 和 checklist.pdf,资源将获得以下名称和标题:
| 文件 | Name | Title |
|---|---|---|
| photo_specs.pdf | pdf-file-1 | 规格说明 #1 |
| other_specs.pdf | pdf-file-2 | 规格说明 #2 |
| guide.pdf | pdf-file-3 | guide.pdf |
| checklist.pdf | pdf-file-4 | checklist.pdf |
优先级规则
当多个规则匹配同一个资源时,只有第一次设置的值会生效。这允许你为特定文件设置特殊属性,然后用通配符设置默认值:
resources:
# 先匹配特定文件(优先级高)
- src: documents/photo_specs.pdf
title: 照片规格说明
params:
icon: photo
# 后匹配通配符(优先级低,作为默认值)
- src: '**.pdf'
params:
icon: pdf
photo_specs.pdf 的 icon 参数将保持为 photo,不会被后续规则覆盖为 pdf。
在模板中使用资源
通过名称获取资源:
<!-- 通过名称精确匹配 -->
{{ with .Resources.Get "header" }}
<img src="{{ .RelPermalink }}" alt="{{ .Title }}">
{{ end }}
<!-- 通过通配符匹配多个资源 -->
{{ range .Resources.Match "*.pdf" }}
<a href="{{ .RelPermalink }}">
{{ with .Params.icon }}<i class="icon-{{ . }}"></i>{{ end }}
{{ .Title }}
</a>
{{ end }}
<!-- 按类型获取资源 -->
{{ range .Resources.ByType "image" }}
{{ $resized := .Resize "800x" }}
<img src="{{ $resized.RelPermalink }}" alt="">
{{ end }}
完整示例:下载资源列表
Front Matter:
---
title: "项目文档"
resources:
- name: user-guide
src: docs/user-guide.pdf
title: 用户指南
params:
icon: pdf
size: large
- name: api-reference
src: docs/api-reference.pdf
title: API 参考文档
params:
icon: pdf
- src: docs/*.md
params:
icon: markdown
---
模板:
{{ define "main" }}
<article>
<h1>{{ .Title }}</h1>
<section class="downloads">
<h2>下载资源</h2>
{{/* 获取特定资源 */}}
{{ with .Resources.Get "user-guide" }}
<div class="download-item">
{{ with .Params.icon }}
<i class="icon-{{ . }}"></i>
{{ end }}
<div class="download-info">
<h3><a href="{{ .RelPermalink }}">{{ .Title }}</a></h3>
{{ with .Params.size }}
<span class="size">文件大小:{{ . }}</span>
{{ end }}
</div>
</div>
{{ end }}
{{/* 获取所有 PDF */}}
<h3>所有 PDF 文档</h3>
<ul>
{{ range .Resources.Match "*.pdf" }}
<li>
<a href="{{ .RelPermalink }}">
{{ with .Params.icon }}<i class="icon-{{ . }}"></i>{{ end }}
{{ .Title | default .Name }}
</a>
</li>
{{ end }}
</ul>
</section>
</article>
{{ end }}
多语言站点中的页面资源
在多语言单主机站点中,Hugo 默认不会复制共享的页面资源,这样可以减少构建时间、存储空间和带宽消耗。
示例结构
content/
└── my-bundle/
├── a.jpg # 共享资源
├── b.jpg # 共享资源
├── c.de.jpg # 德语专用
├── c.en.jpg # 英语专用
├── index.de.md
└── index.en.md
构建后的输出
public/
├── de/
│ └── my-bundle/
│ ├── a.jpg # 共享资源
│ ├── b.jpg # 共享资源
│ ├── c.de.jpg
│ └── index.html
└── en/
└── my-bundle/
├── c.en.jpg # 只有语言专用资源
└── index.html
共享资源 a.jpg 和 b.jpg 只会放在默认语言的目录中,其他语言版本通过渲染钩子正确引用它们。
启用资源复制
如果需要复制共享资源到每个语言目录,可以配置:
[markup]
[markup.goldmark]
duplicateResourceFiles = true
渲染钩子要求
为了正确解析 Markdown 中的链接和图片,需要使用渲染钩子通过 Resources.Get 方法获取资源,然后调用其 RelPermalink 方法。Hugo 默认启用了内置的链接和图片渲染钩子来处理这种情况。
Archetypes(内容原型)
Archetypes 是 Hugo 的内容模板系统,用于在创建新内容时提供预定义的模板。当你运行 hugo new 命令创建新文章时,Hugo 会根据 Archetype 模板自动生成包含预设 Front Matter 的文件,这大大提高了内容创建的效率,并确保了内容格式的一致性。
默认 Archetype 行为
如果不提供任何 Archetype 模板,Hugo 会使用内置的默认模板创建新文件。默认生成的文件只包含三个基本字段:
+++
title = "{{ replace .Name "-" " " | title }}"
date = {{ .Date }}
draft = true
+++
这意味着运行 hugo new posts/my-first-post.md 会生成:
+++
title = "My First Post"
date = 2024-01-15T10:00:00+08:00
draft = true
+++
创建自定义 Archetype
要创建自定义 Archetype,需要在项目根目录下创建 archetypes/ 目录,并在其中放置模板文件。
默认模板
创建 archetypes/default.md 文件,它将应用于所有没有特定 Archetype 的内容:
+++
title = "{{ replace .Name "-" " " | title }}"
date = "{{ .Date }}"
lastmod = "{{ .Date }}"
draft = true
description = ""
author = ""
tags = []
categories = []
image = ""
+++
## 概述
在这里写文章的概述...
## 正文
在这里开始正文内容...
特定 Section 的模板
Hugo 会根据内容的 Section 自动匹配对应的 Archetype。例如,创建 archetypes/posts.md 将专门用于 content/posts/ 目录下的新内容:
+++
title = "{{ replace .Name "-" " " | title }}"
date = "{{ .Date }}"
lastmod = "{{ .Date }}"
draft = true
description = ""
author = ""
tags = []
categories = []
toc = true
comment = true
image = ""
+++
## 简介
<!--more-->
## 详细内容
### 子章节一
### 子章节二
## 总结
当运行 hugo new posts/my-article.md 时,Hugo 会:
- 首先查找
archetypes/posts.md(Section 专用模板) - 如果没找到,使用
archetypes/default.md(默认模板) - 如果都不存在,使用内置默认模板
Archetype 模板语法
Archetype 模板使用 Go 模板语法,可以使用多种变量和函数来动态生成内容。
可用变量
| 变量 | 说明 | 示例 |
|---|---|---|
.Name | 文件名(不含扩展名) | my-first-post |
.Date | 当前日期时间 | 2024-01-15T10:00:00+08:00 |
.Type | 内容类型(Section 名称) | posts |
.Path | 内容相对路径 | posts/my-first-post.md |
.Content | 默认内容(空) | - |
常用模板函数
字符串处理:
+++
{{/* 将连字符替换为空格,并转为标题格式 */}}
title = "{{ replace .Name "-" " " | title }}"
{{/* 转换为大写 */}}
category = "{{ .Type | upper }}"
{{/* 转换为小写 */}}
slug = "{{ .Name | lower }}"
+++
日期格式化:
+++
date = "{{ .Date }}"
year = "{{ .Date.Format "2006" }}"
month = "{{ .Date.Format "01" }}"
dateFormatted = "{{ .Date.Format "2006-01-02" }}"
+++
条件判断:
+++
{{ if eq .Type "posts" }}
type = "article"
{{ else if eq .Type "tutorials" }}
type = "tutorial"
{{ end }}
+++
完整 Archetype 示例
博客文章模板
archetypes/posts.md:
+++
title = "{{ replace .Name "-" " " | title }}"
date = "{{ .Date }}"
lastmod = "{{ .Date }}"
draft = true
description = ""
author = ""
tags = []
categories = []
keywords = []
toc = true
comment = true
readingTime = true
image = ""
+++
<!-- 文章特色图片 -->

## 简介
在这里写文章简介,这部分会显示在列表页面。
<!--more-->
## 正文开始
### 第一部分
内容...
### 第二部分
内容...
## 参考资料
- [参考链接]()
产品页面模板
archetypes/products.md:
+++
title = "{{ replace .Name "-" " " | title }}"
date = "{{ .Date }}"
draft = true
description = ""
price = 0
sku = ""
inStock = true
categories = []
tags = []
images = []
features = []
weight = 0
+++
## 产品概述
产品简介...
## 产品特性
- 特性一
- 特性二
- 特性三
## 规格参数
| 参数 | 值 |
|------|-----|
| 尺寸 | |
| 重量 | {{ .Params.weight }}kg |
| 材质 | |
## 购买信息
价格:¥{{ .Params.price }}
文档页面模板
archetypes/docs.md:
+++
title = "{{ replace .Name "-" " " | title }}"
weight = 1
date = "{{ .Date }}"
draft = true
description = ""
toc = true
prev = ""
next = ""
+++
## 概述
本章介绍...
## 详细说明
### 基本用法
示例代码:
```go
// 代码示例
高级用法
更多内容...
相关链接
### 基于 Bundle 的 Archetype
从 Hugo 0.126 版本开始,Archetype 可以是目录形式,用于创建 Page Bundle。这在需要同时创建内容文件和相关资源时非常有用。
#### 目录结构
archetypes/ ├── default.md ├── posts.md └── gallery/ # 目录形式的 Archetype ├── index.md # 内容模板 └── cover.jpg # 预置资源文件
#### 使用目录 Archetype
```bash
# 使用 gallery 目录作为模板
hugo new gallery/my-album
这会创建:
content/
└── gallery/
└── my-album/
├── index.md
└── cover.jpg
目录 Archetype 模板
archetypes/gallery/index.md:
+++
title = "{{ replace .Name "-" " " | title }}"
date = "{{ .Date }}"
draft = true
images = []
+++
## 相册描述
照片说明...
## 照片列表
{{ range .Resources.ByType "image" }}

{{ end }}
在主题中提供 Archetype
主题开发者可以在主题目录中提供默认的 Archetype 模板。Hugo 会按照以下优先级查找 Archetype:
- 项目根目录的
archetypes/ - 主题目录的
archetypes/ - Hugo 内置默认模板
主题目录结构:
themes/my-theme/
└── archetypes/
├── default.md
├── posts.md
└── docs.md
用户可以通过在项目 archetypes/ 目录中创建同名文件来覆盖主题的 Archetype。
创建内容的命令详解
# 基本语法
hugo new <内容路径>
# 在 posts section 创建文章
hugo new posts/my-article.md
# 创建 Page Bundle
hugo new posts/my-post/index.md
# 使用特定 kind/archetype
hugo new --kind posts articles/tutorial.md
# 创建指定格式的内容
hugo new --format yaml posts/article.md
# 强制覆盖已存在的文件
hugo new --force posts/existing.md
Archetype 最佳实践
1. 使用 YAML 格式提高可读性
虽然 TOML 是 Hugo 的默认格式,但 YAML 在处理列表和嵌套结构时更清晰:
---
title: "{{ replace .Name "-" " " | title }}"
date: {{ .Date }}
draft: true
tags:
-
categories:
-
author: ""
---
2. 添加注释帮助编辑
在 Archetype 中添加注释可以帮助内容编辑者理解每个字段的用途:
+++
title = "{{ replace .Name "-" " " | title }}"
date = "{{ .Date }}"
lastmod = "{{ .Date }}" # 最后修改日期,通常与 date 相同
draft = true # 设为 false 后文章才会发布
description = "" # 用于 SEO 的描述文字
author = "" # 作者名称
tags = [] # 标签,如 ["Hugo", "教程"]
categories = [] # 分类,如 ["技术文档"]
toc = true # 是否显示目录
comment = true # 是否开启评论
+++
3. 预设内容结构
为特定类型的内容预设合理的结构,帮助作者快速开始写作:
+++
title = "{{ replace .Name "-" " " | title }}"
date = "{{ .Date }}"
draft = true
+++
## 问题背景
描述遇到的问题...
## 解决方案
### 方案一
### 方案二
## 最佳实践
## 总结
4. 避免过度复杂的模板
Archetype 应该简洁实用,不要添加太多可能不需要的字段:
+++
{{/* 推荐:只包含常用字段 */}}
title = "{{ replace .Name "-" " " | title }}"
date = "{{ .Date }}"
draft = true
tags = []
+++
{{/* 不推荐:包含太多可能不用的字段 */}}
+++
title = "{{ replace .Name "-" " " | title }}"
date = "{{ .Date }}"
lastmod = "{{ .Date }}"
publishDate = "{{ .Date }}"
expiryDate = ""
draft = true
description = ""
keywords = []
tags = []
categories = []
author = ""
email = ""
phone = ""
address = ""
custom1 = ""
custom2 = ""
+++
创建内容
使用命令创建
# 创建文章
hugo new posts/my-article.md
# 创建页面捆绑
hugo new posts/my-post/index.md
# 创建 about 页面
hugo new about.md
手动创建
直接在 content 目录下创建 Markdown 文件即可。
内容格式
Hugo 支持 Markdown 和其他格式。
Markdown
默认格式,使用 Goldmark 解析器:
## 标题
这是**粗体**和*斜体*。
- 列表项 1
- 列表项 2
[链接](https://example.com)

`行内代码`
```python
def hello():
print("Hello, Hugo!")
**Org mode**
需要配置:
```toml
[markup]
defaultMarkdownHandler = "org"
内容摘要
Hugo 提供多种方式生成内容摘要。
自动摘要
默认情况下,Hugo 取内容的前 70 个单词作为摘要。
手动摘要分割
使用 <!--more--> 标记摘要结束位置:
+++
title = "我的文章"
+++
这是文章的摘要部分,会显示在列表页。
<!--more-->
这是文章的正文部分,只在详情页显示。
自定义摘要
在 Front Matter 中指定:
+++
title = "我的文章"
summary = "这是自定义的文章摘要。"
+++
在模板中使用摘要
<!-- 显示摘要 -->
{{ .Summary }}
<!-- 显示阅读更多链接 -->
{{ if .Truncated }}
<a href="{{ .RelPermalink }}">阅读更多</a>
{{ end }}
内容排序
Hugo 支持多种排序方式。
默认排序
默认按权重、日期排序:
{{ range .Site.RegularPages }}
<!-- 按 weight, date 排序 -->
{{ end }}
自定义排序
<!-- 按日期降序 -->
{{ range .Site.RegularPages.ByDate.Reverse }}
<h2>{{ .Title }}</h2>
{{ end }}
<!-- 按标题排序 -->
{{ range .Site.RegularPages.ByTitle }}
<h2>{{ .Title }}</h2>
{{ end }}
<!-- 按权重排序 -->
{{ range .Site.RegularPages.ByWeight }}
<h2>{{ .Title }}</h2>
{{ end }}
<!-- 按发布日期排序 -->
{{ range .Site.RegularPages.ByPublishDate.Reverse }}
<h2>{{ .Title }}</h2>
{{ end }}
<!-- 按最后修改日期排序 -->
{{ range .Site.RegularPages.ByLastmod.Reverse }}
<h2>{{ .Title }}</h2>
{{ end }}
<!-- 按长度排序 -->
{{ range .Site.RegularPages.ByLength }}
<h2>{{ .Title }}</h2>
{{ end }}
使用 weight 控制排序
+++
title = "第一章"
weight = 1
+++
+++
title = "第二章"
weight = 2
+++
内容分组
按日期分组
{{ range .Site.RegularPages.GroupByDate "2006-01" }}
<h2>{{ .Key }}</h2>
<ul>
{{ range .Pages }}
<li><a href="{{ .RelPermalink }}">{{ .Title }}</a></li>
{{ end }}
</ul>
{{ end }}
按分类分组
{{ range .Site.Taxonomies.tags }}
<h2>{{ .Page.Title }}</h2>
<ul>
{{ range .Pages }}
<li><a href="{{ .RelPermalink }}">{{ .Title }}</a></li>
{{ end }}
</ul>
{{ end }}
相关内容
Hugo 可以根据标签、分类等推荐相关内容。
{{ $related := .Site.RegularPages.Related . | first 5 }}
{{ with $related }}
<h3>相关文章</h3>
<ul>
{{ range . }}
<li><a href="{{ .RelPermalink }}">{{ .Title }}</a></li>
{{ end }}
</ul>
{{ end }}
配置相关内容算法:
[related]
includeNewer = true
threshold = 80
toLower = false
[[related.indices]]
name = "tags"
weight = 100
[[related.indices]]
name = "categories"
weight = 80
[[related.indices]]
name = "date"
weight = 10
跨页面引用
ref 和 relref
引用其他页面的永久链接:
<!-- 使用 ref 获取绝对 URL -->
查看[关于页面]({{< ref "about.md" >}})
<!-- 使用 relref 获取相对 URL -->
查看[关于页面]({{< relref "about.md" >}})
<!-- 引用特定语言版本 -->
查看[关于页面]({{< ref "about.en.md" >}})
在模板中引用
{{ with .Site.GetPage "about" }}
<a href="{{ .RelPermalink }}">{{ .Title }}</a>
{{ end }}
{{ with .Site.GetPage "posts/my-post" }}
<a href="{{ .RelPermalink }}">{{ .Title }}</a>
{{ end }}
菜单系统
定义菜单
在 Front Matter 中定义:
+++
title = "关于"
menu = "main"
+++
或详细配置:
+++
title = "关于"
[menu.main]
name = "关于我们"
weight = 3
identifier = "about"
+++
在配置文件中定义:
[menu]
[[menu.main]]
name = "首页"
url = "/"
weight = 1
[[menu.main]]
name = "博客"
url = "/posts/"
weight = 2
[[menu.main]]
name = "关于"
url = "/about/"
weight = 3
在模板中使用菜单
<nav>
<ul>
{{ range .Site.Menus.main }}
<li>
<a href="{{ .URL }}">{{ .Name }}</a>
{{ if .HasChildren }}
<ul>
{{ range .Children }}
<li><a href="{{ .URL }}">{{ .Name }}</a></li>
{{ end }}
</ul>
{{ end }}
</li>
{{ end }}
</ul>
</nav>
多语言内容
文件命名约定
content/
├── posts/
│ ├── first-post.en.md # 英文版
│ ├── first-post.zh.md # 中文版
│ └── first-post.ja.md # 日文版
配置多语言
defaultContentLanguage = "zh"
[languages]
[languages.zh]
title = "我的博客"
languageName = "中文"
weight = 1
[languages.en]
title = "My Blog"
languageName = "English"
weight = 2
访问翻译链接
{{ range .Translations }}
<a href="{{ .RelPermalink }}">{{ .Language.LanguageName }}</a>
{{ end }}
多语言内容目录
除了文件名后缀方式,也可以使用独立的内容目录管理多语言内容:
[languages]
[languages.zh]
contentDir = 'content/zh'
languageName = '中文'
weight = 1
[languages.en]
contentDir = 'content/en'
languageName = 'English'
weight = 2
目录结构:
content/
├── zh/
│ └── posts/
│ └── first-post.md
└── en/
└── posts/
└── first-post.md
使用 translationKey 关联翻译
当翻译页面的路径不同时,可以使用 translationKey 手动关联。这在以下场景特别有用:
场景一:不同语言的 URL 路径不同
不同语言习惯可能使用不同的 URL 结构:
<!-- content/about-us.en.md -->
---
title: "About Us"
translationKey: "about"
---
<!-- content/a-propos.fr.md -->
---
title: "À Propos"
translationKey: "about"
---
<!-- content/关于我们.zh.md -->
---
title: "关于我们"
translationKey: "about"
---
这三个页面虽然路径不同,但通过相同的 translationKey 被关联为翻译页面。
场景二:多语言内容目录结构
当使用独立内容目录时,如果文件名或路径不完全匹配:
content/
├── en/
│ └── company/
│ └── about-us.md # translationKey: "about"
├── zh/
│ └── company/
│ └── guanyu.md # translationKey: "about"
└── fr/
└── societe/
└── a-propos.md # translationKey: "about"
场景三:跨越不同分区
有时同一内容在不同语言的分区位置不同:
<!-- content/en/products/widget.md -->
---
title: "Widget Product"
translationKey: "product-widget"
---
<!-- content/zh/solutions/widget.md -->
---
title: "Widget 解决方案"
translationKey: "product-widget"
---
获取翻译链接的完整示例
在模板中显示语言切换器:
{{ if .IsTranslated }}
<nav class="language-switcher">
<span>当前语言:{{ .Language.LanguageName }}</span>
<ul>
{{ range .AllTranslations }}
<li>
<a href="{{ .RelPermalink }}"
hreflang="{{ .Language.Lang }}"
lang="{{ .Language.Lang }}">
{{ .Language.LanguageName }}
</a>
</li>
{{ end }}
</ul>
</nav>
{{ end }}
结合 i18n 使用
配合翻译文件实现本地化:
{{ if .IsTranslated }}
<div class="translations">
<span>{{ i18n "availableIn" }}:</span>
{{ range .Translations }}
<a href="{{ .RelPermalink }}" lang="{{ .Language.Lang }}">
{{ .Language.LanguageName }}
</a>
{{ end }}
</div>
{{ end }}
i18n/en.toml:
[availableIn]
other = "Also available in"
i18n/zh.toml:
[availableIn]
other = "其他语言版本"
最佳实践
- 命名规范:使用有意义且一致的 translationKey,如
about、contact、product-widget - 避免冲突:确保不同内容的 translationKey 不会重复
- 文档记录:在团队中建立 translationKey 的命名规范文档
- 检查链接:定期检查翻译链接是否正确关联
Cascade 级联配置
Cascade 允许父页面将 Front Matter 配置传递给所有后代页面。这是管理大量内容时非常强大的功能。
基本用法
在首页为所有后代页面设置默认参数:
<!-- content/_index.md -->
---
title: "首页"
cascade:
params:
showToc: true
showComments: true
---
所有页面的 .Params.showToc 和 .Params.showComments 都会继承这些值,除非页面自身定义了这些参数。
为特定分区设置
通过 target 关键字可以针对特定路径的页面设置级联配置:
---
title: "首页"
cascade:
params:
layout: "docs"
showBreadcrumb: true
target:
path: '{/docs,/docs/**}'
---
这只会影响 /docs/ 路径下的所有页面。
多目标级联
可以为不同的目标设置不同的级联值:
---
title: "首页"
cascade:
- params:
layout: "blog"
showAuthor: true
target:
kind: page
path: '{/blog/**}'
- params:
layout: "docs"
showToc: true
target:
kind: page
path: '{/docs/**}'
---
Target 目标选项
target 支持以下过滤条件:
| 选项 | 说明 | 示例 |
|---|---|---|
path | 页面路径匹配 | '{/posts,/posts/**}' |
kind | 页面类型匹配 | '{taxonomy,term}' |
environment | 构建环境匹配 | '{staging,production}' |
这些条件可以组合使用:
cascade:
params:
analytics: true
target:
path: '{/posts/**}'
environment: production
级联优先级
当多个页面定义了级联配置时,优先级如下:
- 页面自身的 Front Matter 定义(最高优先级)
- 最近祖先的级联配置
- 更远祖先的级联配置
也就是说,后代页面的配置会覆盖祖先页面的级联值。
实际应用场景
统一文档布局
为文档分区设置统一布局:
<!-- content/docs/_index.md -->
---
title: "文档"
cascade:
layout: "docs"
params:
showToc: true
showSidebar: true
showBreadcrumb: true
---
控制发布行为
为特定内容设置发布规则:
<!-- content/_index.md -->
---
cascade:
build:
render: always
list: always
target:
path: '{/drafts/**}'
build:
render: never
---
Markdown 属性
Markdown 属性是 Hugo 的一个强大功能,允许你在渲染 Markdown 时添加 HTML 属性。这让你可以在不直接写 HTML 的情况下,为元素添加 class、id 等属性。
基本语法
在 Markdown 元素后使用 {} 添加属性:
这是一个段落。{#my-id .my-class}
# 标题 {.text-center #main-title}
 {width="800" height="600"}
[链接](https://example.com) {target="_blank" rel="noopener"}
块级元素属性
段落属性:
这是一段文字。
{.highlight .important}
另一段文字。
{#intro-paragraph style="color: blue;"}
渲染结果:
<p class="highlight important">这是一段文字。</p>
<p id="intro-paragraph" style="color: blue;">另一段文字。</p>
标题属性:
# 第一章 {.chapter-title #chapter-1}
## 1.1 简介 {.section-title}
列表属性:
- 第一项
- 第二项
- 第三项
{.numbered .indent}
代码块属性:
```python {data-lang="Python" .highlight}
def hello():
print("Hello")
### 行内元素属性
**链接属性**:
```markdown
[访问官网](https://example.com){target="_blank" class="external-link"}
[下载文件](/files/doc.pdf){download="doc.pdf"}
图片属性:
{width="800" height="400" .cover-image}
{.rounded .shadow}
行内代码属性:
`const x = 1;`{.language-javascript}
`npm install hugo`{.command}
配置启用
确保 Goldmark 配置允许属性:
[markup]
[markup.goldmark]
[markup.goldmark.parser]
attribute = true # 启用属性支持
实际应用示例
警告框:
注意:这是一个重要提示。
{.alert .alert-warning}
配合 CSS:
.alert {
padding: 1rem;
border-radius: 4px;
margin: 1rem 0;
}
.alert-warning {
background: #fef3c7;
border-left: 4px solid #f59e0b;
color: #92400e;
}
图片网格:
{.grid-item}
{.grid-item}
{.grid-item}
{.image-grid}
CSS:
.image-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1rem;
}
.grid-item {
width: 100%;
height: auto;
border-radius: 8px;
}
数学公式
Hugo 支持在 Markdown 中编写数学公式,使用 LaTeX 语法。从 Hugo 0.122 版本开始,Hugo 内置了 KaTeX 渲染引擎,可以在构建时直接将数学公式转换为 HTML 或 MathML,无需依赖客户端 JavaScript。
方式一:服务端渲染(推荐)
Hugo 0.122+ 内置了 KaTeX 渲染引擎,使用 transform.ToMath 函数在构建时渲染公式。这是最推荐的方式,因为:
- 无需客户端 JavaScript,页面加载更快
- 对搜索引擎更友好
- 支持离线阅读
- 输出结果更稳定
第一步:配置 Goldmark passthrough 扩展
在 hugo.toml 中启用 passthrough 扩展,使 Hugo 保留 LaTeX 标记不被 Markdown 解析器处理:
[markup]
[markup.goldmark]
[markup.goldmark.extensions]
[markup.goldmark.extensions.passthrough]
enable = true
[markup.goldmark.extensions.passthrough.delimiters]
block = [['\[', '\]'], ['$$', '$$']]
inline = [['\(', '\)']]
这个配置的含义是:
block:块级公式的分隔符,使用\[...\]或$$...$$inline:行内公式的分隔符,使用\(...\)
第二步:创建 passthrough 渲染钩子
layouts/_default/_markup/render-passthrough.html:
{{- $opts := dict "output" "htmlAndMathml" "displayMode" (eq .Type "block") }}
{{- with try (transform.ToMath .Inner $opts) }}
{{- with .Err }}
{{- errorf "无法渲染数学公式: %s (位置: %s)" . $.Position }}
{{- else }}
{{- .Value }}
{{- $.Page.Store.Set "hasMath" true }}
{{- end }}
{{- end -}}
这个渲染钩子使用 try 函数安全地调用 transform.ToMath,如果出错会显示详细的错误信息。
第三步:在基础模板中加载 CSS
在 layouts/_default/baseof.html 的 <head> 中添加:
<head>
{{ $noop := .WordCount }}
{{ if .Page.Store.Get "hasMath" }}
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/katex.min.css" rel="stylesheet">
{{ end }}
</head>
注意:$noop := .WordCount 这行代码很重要,它强制 Hugo 在检查 hasMath 标志之前先渲染页面内容,确保标志被正确设置。
transform.ToMath 函数选项
transform.ToMath 函数接受两个参数:公式内容和选项映射。
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
displayMode | bool | false | 是否为块级公式 |
output | string | mathml | 输出格式:mathml、html、htmlAndMathml |
errorColor | string | #cc0000 | 错误信息颜色 |
throwOnError | bool | true | 遇到错误时是否抛出异常 |
macros | map | 自定义宏定义 | |
fleqn | bool | false | 是否左对齐公式 |
minRuleThickness | float | 0.04 | 分数线最小粗细 |
使用示例
<!-- 基本用法:输出 MathML -->
{{ transform.ToMath "c = \\pm\\sqrt{a^2 + b^2}" }}
<!-- 指定输出格式(需要加载 CSS) -->
{{ $opts := dict "output" "htmlAndMathml" }}
{{ transform.ToMath "E = mc^2" $opts }}
<!-- 使用自定义宏 -->
{{ $macros := dict "\\addBar" "\\bar{#1}" "\\bold" "\\mathbf{#1}" }}
{{ $opts := dict "macros" $macros }}
{{ transform.ToMath "\\addBar{y} + \\bold{H}" $opts }}
<!-- 块级公式 -->
{{ $opts := dict "displayMode" true "output" "htmlAndMathml" }}
{{ transform.ToMath "\\int_0^\\infty e^{-x^2} dx = \\frac{\\sqrt{\\pi}}{2}" $opts }}
化学公式支持(Hugo 0.144+)
从 Hugo 0.144 版本开始,transform.ToMath 还支持渲染化学方程式,使用 mhchem 包的 \ce 和 \pu 函数:
$$C_p[\ce{H2O(l)}] = \pu{75.3 J // mol K}$$
方式二:客户端渲染
如果需要使用客户端 JavaScript 渲染公式(例如需要 MathJax 的特定功能),可以使用以下方式:
MathJax 集成
{{ if .Param "math" }}
<script>
MathJax = {
tex: {
inlineMath: [['\\(', '\\)']],
displayMath: [['\\[', '\\]'], ['$$', '$$']]
}
};
</script>
<script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js" async></script>
{{ end }}
KaTeX 客户端渲染
{{ if .Param "math" }}
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/katex.min.css">
<script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/katex.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/contrib/auto-render.min.js"
onload="renderMathInElement(document.body, {
delimiters: [
{left: '$$', right: '$$', display: true},
{left: '\\(', right: '\\)', display: false},
{left: '\\[', right: '\\]', display: true}
]
});"></script>
{{ end }}
在 Markdown 中使用公式
行内公式
使用 \(...\) 包裹:
爱因斯坦质能方程 \(E = mc^2\) 是物理学中最著名的公式之一。
圆的面积公式为 \(A = \pi r^2\),其中 \(r\) 是半径。
块级公式
使用 \[...\] 或 $$...$$ 包裹:
高斯积分公式:
\[
\int_{-\infty}^{\infty} e^{-x^2} dx = \sqrt{\pi}
\]
或者使用双美元符号:
$$
\sum_{i=1}^{n} i = \frac{n(n+1)}{2}
$$
常用公式示例
分数和根号:
$$
\frac{-b \pm \sqrt{b^2 - 4ac}}{2a}
$$
渲染结果:
求和与积分:
$$
\sum_{i=1}^{n} i = \frac{n(n+1)}{2}
$$
$$
\int_a^b f(x) dx = F(b) - F(a)
$$
矩阵:
$$
\begin{bmatrix}
a & b \\
c & d
\end{bmatrix}
$$
方程组:
$$
\begin{cases}
x + y = 10 \\
2x - y = 5
\end{cases}
$$
希腊字母:
$$
\alpha, \beta, \gamma, \delta, \epsilon, \theta, \lambda, \mu, \pi, \sigma, \omega
$$
上下标:
$$
x^2 + y^2 = z^2
$$
$$
a_1 + a_2 + a_3 + \cdots + a_n
$$
极限:
$$
\lim_{x \to 0} \frac{\sin x}{x} = 1
$$
偏导数:
$$
\frac{\partial f}{\partial x} = \lim_{h \to 0} \frac{f(x+h) - f(x)}{h}
$$
在代码块中使用
对于复杂的公式,可以使用代码块格式:
```math
f(x) = \begin{cases}
x^2 & \text{if } x \geq 0 \\
-x^2 & \text{if } x < 0
\end{cases}
### 数学公式样式
添加 CSS 改善公式显示:
```css
/* 块级公式居中 */
.math-display {
display: block;
text-align: center;
margin: 1.5rem 0;
overflow-x: auto;
padding: 1rem;
background: #f8fafc;
border-radius: 4px;
}
/* 行内公式 */
.math-inline {
padding: 0 0.2rem;
}
/* 深色模式 */
@media (prefers-color-scheme: dark) {
.math-display {
background: #1e293b;
}
}
常见问题
公式不显示:
- 确保使用了正确的分隔符
$...$或$$...$$ - 检查 MathJax/KaTeX 库是否正确加载
- 确认 Goldmark 配置允许 HTML
特殊字符转义: 某些字符在 Markdown 中有特殊含义,需要转义:
使用 \\{ 和 \\} 代替 { 和 }
在属性中使用公式: 避免在 HTML 属性中使用复杂的公式,可能导致解析错误。
构建选项
Build Options 允许你控制 Hugo 在构建站点时如何处理特定页面。这些选项通过 Front Matter 中的 build 对象配置。
基本配置
---
title: "我的文章"
build:
render: always
list: always
publishResources: true
---
render 选项
控制是否渲染页面:
| 值 | 说明 |
|---|---|
always | 总是渲染页面到磁盘(默认) |
never | 不渲染页面到磁盘,但分配 Permalink 和 RelPermalink |
list 选项
控制页面是否包含在页面集合中:
| 值 | 说明 |
|---|---|
always | 包含在所有页面集合中(默认) |
local | 只包含在本地页面集合中(如 .Pages、.RegularPages) |
never | 不包含在任何页面集合中 |
publishResources 选项
控制页面资源是否发布:
| 值 | 说明 |
|---|---|
true | 总是发布资源(默认) |
false | 只有在模板中调用 .Permalink、.RelPermalink 或 .Content 方法时才发布 |
实用示例
创建无头页面
无头页面不会被渲染为 HTML,但其内容可以在其他页面中使用:
---
title: "共享组件"
build:
render: never
list: local
---
在模板中引用:
{{ with .Site.GetPage "shared-components" }}
{{ .Content }}
{{ range .Resources }}
<img src="{{ .RelPermalink }}">
{{ end }}
{{ end }}
创建无头分区
整个分区都不发布,但内容可用:
---
title: "内部文档"
cascade:
build:
render: never
list: local
---
按条件隐藏内容
只在开发环境显示某些内容:
---
title: "内部指南"
cascade:
build:
render: never
target:
environment: production
---
发布但不列入列表
发布页面但不显示在列表中:
---
title: "隐藏页面"
build:
list: never
---
列出不发布
创建列表页但不发布单独页面:
---
title: "术语表"
cascade:
build:
render: never
list: local
---
小结
本章介绍了 Hugo 内容管理的核心知识:
- Front Matter 定义页面元数据
- Section 和 Taxonomy 组织内容
- Page Bundle 管理页面资源
- 摘要、排序和分组功能
- 菜单系统和多语言支持
- 构建选项控制页面渲染行为
下一章将学习 Hugo 模板系统,掌握如何自定义页面布局。