跳到主要内容

内容管理

内容是 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自定义摘要"这是摘要"
slugURL 别名"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 是用户定义的内容分类系统,默认支持 tagscategories

配置分类法

# 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 中引用资源:

![封面](cover.jpg)
![照片](images/photo.jpg)

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资源名称,用于 MatchGetGetMatch 方法匹配
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 是一个特殊的占位符,用于在 nametitle 参数中自动生成序号:

resources:
- src: '*specs.pdf'
title: '规格说明 #:counter'

- name: 'pdf-file-:counter'
src: '**.pdf'

假设页面包含 photo_specs.pdfother_specs.pdfguide.pdfchecklist.pdf,资源将获得以下名称和标题:

文件NameTitle
photo_specs.pdfpdf-file-1规格说明 #1
other_specs.pdfpdf-file-2规格说明 #2
guide.pdfpdf-file-3guide.pdf
checklist.pdfpdf-file-4checklist.pdf

优先级规则

当多个规则匹配同一个资源时,只有第一次设置的值会生效。这允许你为特定文件设置特殊属性,然后用通配符设置默认值:

resources:
# 先匹配特定文件(优先级高)
- src: documents/photo_specs.pdf
title: 照片规格说明
params:
icon: photo

# 后匹配通配符(优先级低,作为默认值)
- src: '**.pdf'
params:
icon: pdf

photo_specs.pdficon 参数将保持为 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.jpgb.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 会:

  1. 首先查找 archetypes/posts.md(Section 专用模板)
  2. 如果没找到,使用 archetypes/default.md(默认模板)
  3. 如果都不存在,使用内置默认模板

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 = ""
+++

<!-- 文章特色图片 -->
![封面图](cover.jpg)

## 简介

在这里写文章简介,这部分会显示在列表页面。

<!--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" }}
![{{ .Name }}]({{ .Name }})
{{ end }}

在主题中提供 Archetype

主题开发者可以在主题目录中提供默认的 Archetype 模板。Hugo 会按照以下优先级查找 Archetype:

  1. 项目根目录的 archetypes/
  2. 主题目录的 archetypes/
  3. 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)

![图片](image.jpg)

`行内代码`

```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 = "其他语言版本"

最佳实践

  1. 命名规范:使用有意义且一致的 translationKey,如 aboutcontactproduct-widget
  2. 避免冲突:确保不同内容的 translationKey 不会重复
  3. 文档记录:在团队中建立 translationKey 的命名规范文档
  4. 检查链接:定期检查翻译链接是否正确关联

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

级联优先级

当多个页面定义了级联配置时,优先级如下:

  1. 页面自身的 Front Matter 定义(最高优先级)
  2. 最近祖先的级联配置
  3. 更远祖先的级联配置

也就是说,后代页面的配置会覆盖祖先页面的级联值。

实际应用场景

统一文档布局

为文档分区设置统一布局:

<!-- 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}

![图片](photo.jpg) {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"}

图片属性

![封面图](cover.jpg){width="800" height="400" .cover-image}

![头像](avatar.png){.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;
}

图片网格

![图1](img1.jpg){.grid-item}
![图2](img2.jpg){.grid-item}
![图3](img3.jpg){.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 函数接受两个参数:公式内容和选项映射。

选项类型默认值说明
displayModeboolfalse是否为块级公式
outputstringmathml输出格式:mathmlhtmlhtmlAndMathml
errorColorstring#cc0000错误信息颜色
throwOnErrorbooltrue遇到错误时是否抛出异常
macrosmap自定义宏定义
fleqnboolfalse是否左对齐公式
minRuleThicknessfloat0.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}
$$

渲染结果: b±b24ac2a\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 内容管理的核心知识:

  1. Front Matter 定义页面元数据
  2. Section 和 Taxonomy 组织内容
  3. Page Bundle 管理页面资源
  4. 摘要、排序和分组功能
  5. 菜单系统和多语言支持
  6. 构建选项控制页面渲染行为

下一章将学习 Hugo 模板系统,掌握如何自定义页面布局。