Hugo Theme Stack 对默认 Taxonomy 的处理

核心结论

Stack 没有重新实现 Taxonomy 系统。Hugo 负责读取文章 Front Matter、建立 Taxonomy 与 Term 的关联、生成页面对象和 URL;Stack 负责调用 Hugo 提供的 .Site.Taxonomies.GetTerms.Pages,将这些数据展示为分类徽章、文章标签、侧栏 Widget、归档入口以及列表页面。

可以把整体流程理解为:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
文章 Front Matter
Hugo 建立 Taxonomy 数据
    ├── 生成 Taxonomy 根页面
    ├── 生成 Term 页面
    ├── 建立 Term 与文章的关联
    └── 提供 .Site.Taxonomies、.GetTerms、.Pages
Stack 负责选择模板和渲染界面

Hugo 默认的 Taxonomy

在没有显式配置 [taxonomies] 时,Hugo 默认启用 Category 和 Tag:

1
2
3
[taxonomies]
category = "categories"
tag = "tags"

左侧的 categorytag 是 Taxonomy 的 singular 名称,右侧的 categoriestags 是 plural 名称。文章 Front Matter 使用 plural 字段声明关联:

1
2
3
4
5
categories:
  - Hugo
tags:
  - Stack
  - Theme

Hugo 构建后会生成相应的页面对象和默认路径:

页面Page Kind含义
/categories/taxonomy所有 Category Term 的集合
/categories/hugo/term属于 Hugo Category 的文章集合
/tags/taxonomy所有 Tag Term 的集合
/tags/stack/term带有 Stack Tag 的文章集合

这些页面的数据来自文章 Front Matter,而不是来自 content/categoriescontent/tags 目录。

Hugo 与 Stack 的职责边界

能力HugoStack
解析文章中的 categoriestags负责不负责
建立 Term 与文章的关联负责不负责
生成 Taxonomy 和 Term Page负责不负责
生成默认 Taxonomy URL负责不负责
选择最终模板按 Template Lookup 执行提供候选模板
展示分类徽章和标签提供数据负责
展示分类和标签 Widget提供 .Site.Taxonomies负责
设计分类颜色、卡片和布局不负责负责

因此,Stack 不会扫描文章并自行统计标签。它只是消费 Hugo 构建完成的内容模型。

Stack 如何渲染分类

Stack 在文章详情组件中通过下面的方式读取当前文章的分类:

1
{{ range $Page.GetTerms "categories" }}

对应源码为:

1
theme/hugo-theme-stack/layouts/_partials/article/components/details.html

.GetTerms "categories" 返回的不是普通字符串,而是当前文章关联的 Category Term Page。Stack 可以直接读取每个 Term 的 .LinkTitle.RelPermalink.Params,将分类渲染成指向 /categories/<term>/ 的徽章。

分类徽章默认根据 Term 名称计算颜色。分类 Term 的 Front Matter 也可以通过 style.backgroundstyle.color 覆盖默认颜色:

1
2
3
4
5
6
---
title: Hugo
style:
  background: "#2a9d8f"
  color: "#fff"
---

Stack 提供的 Category archetype 也包含这些字段:

1
theme/hugo-theme-stack/archetypes/categories.md

Stack 如何渲染标签

文章详情页通过下面的调用读取标签:

1
{{ range $Page.GetTerms "tags" }}

对应源码为:

1
theme/hugo-theme-stack/layouts/_partials/article/components/tags.html

Stack 通常把标签显示在文章正文后面。文章列表中的标签是可选内容,由 article.list.showTags 控制:

1
2
[article.list]
showTags = true

Stack 对 Category 和 Tag 的定位并不完全相同。Category 更接近文章的主要归类,会显示为醒目的彩色徽章;Tag 更接近辅助检索信息,通常显示在文章底部或标签云中。这是主题的界面设计差异,不是 Hugo 数据模型上的能力差异。

Taxonomy 和 Term 页面如何选择模板

Stack 没有分别为 categoriestags 实现一套完整的专用列表模板。Hugo 按照 Template Lookup Order 查找模板后,通常会回退到 Stack 的通用列表模板:

1
theme/hugo-theme-stack/layouts/list.html

Stack 的页面筛选辅助模板明确处理 sectiontaxonomyterm

1
{{ if in (slice "section" "taxonomy" "term") .Kind }}

对应源码为:

1
theme/hugo-theme-stack/layouts/_partials/helper/pages.html

不同 Page Kind 进入同一套列表界面后,数据含义不同:

1
2
3
4
5
6
7
/tags/
    Kind = taxonomy
    .Pages = 所有 Tag Term Page

/tags/hugo/
    Kind = term
    .Pages = 所有带有 hugo 标签的文章

Stack 的 list.html 再统一负责标题卡片、描述、封面、子项、文章列表和分页等界面。

content/tagscontent/categories 的作用

推荐的可选元信息结构是:

1
2
3
4
5
6
7
8
9
content/
├── categories/
│   ├── _index.md
│   └── hugo/
│       └── _index.md
└── tags/
    ├── _index.md
    └── stack/
        └── _index.md

各文件的作用如下:

文件补充元信息的页面
content/categories/_index.md/categories/ Taxonomy 根页面
content/categories/hugo/_index.md/categories/hugo/ Term 页面
content/tags/_index.md/tags/ Taxonomy 根页面
content/tags/stack/_index.md/tags/stack/ Term 页面

这些 _index.md 可以设置 titledescriptionimage、菜单信息以及分类样式,但不会创建文章关联。即使不存在这些文件,只要 Taxonomy 已启用且文章声明了相应 Term,Hugo 仍能创建 Taxonomy 和 Term 页面。

反过来,仅创建下面的文件并不会自动让文章拥有 Stack 标签:

1
content/tags/stack/_index.md

文章仍然需要显式声明:

1
2
tags:
  - Stack

为什么必须使用 _index.md

Taxonomy 根页面和 Term 页面都是集合节点,因此对应的元信息文件应使用 _index.md。例如:

1
content/tags/_index.md

如果改成:

1
content/tags/index.md

这个目录会被声明为 Leaf Bundle,也就是普通内容 Page,可能与 Hugo 自动生成的 /tags/ Taxonomy Page 产生内容模型和模板查找冲突。

同理,把标签页放到下面的位置:

1
content/page/tags/index.md

创建的是 Kind=pageType=page 的普通功能页面,不是 Hugo 的 Kind=taxonomy 页面。即使最终 URL 被 slug 或 permalink 改成 /tags/,它在内容模型中仍然不是 Tag Taxonomy 根节点。

layout: tags 也不能把普通 Page 转换成 Taxonomy。layout 只参与模板选择,页面的 Kind 仍由内容结构和 Hugo 的 Taxonomy 机制决定。

侧栏 Widget 如何读取 Taxonomy

当前站点启用了 Category 和 Tag Cloud Widget:

1
2
3
4
5
6
[widgets]
homepage = [
  {type = "search"},
  {type = "categories", params = {limit = 15}},
  {type = "tag-cloud", params = {limit = 20}},
]

Category Widget 和 Tag Cloud Widget 最终都会调用 Stack 的通用 Taxonomy Widget:

1
theme/hugo-theme-stack/layouts/_partials/widget/taxonomy.html

核心读取逻辑是:

1
{{ $taxonomy := index $context.Site.Taxonomies .Params.taxonomy }}

然后通过 $taxonomy.ByCount 按文章数量排列 Term。因此,Widget 的统计数据来自 .Site.Taxonomies,并不依赖 content/tags/_index.mdcontent/categories/_index.md

归档页对 Category 的特殊处理

Stack 的归档模板会额外读取 Category Taxonomy Page,并在归档页上展示分类入口:

1
theme/hugo-theme-stack/layouts/archives.html

其核心逻辑会获取 categories 的 Taxonomy 根页面。Tag 没有进入同一段归档页逻辑,因此 Category 在 Stack 中比 Tag 多了一个归档入口。这仍然只是主题展示策略,不代表 Hugo 对 Category 和 Tag 的处理能力不同。

相关文章与 Taxonomy 的关系

Stack 的相关文章组件只是调用 Hugo 的 Related Content API:

1
{{ $related := (.Site.RegularPages.Related .) | first 5 }}

对应源码为:

1
theme/hugo-theme-stack/layouts/_partials/article/components/related-content.html

tagscategories 是否参与相似度计算、各自权重是多少,由 Hugo 的 Related Content 配置决定,不是 Stack 在模板中硬编码的行为。不能因为 Stack 展示了分类和标签,就直接推断相关文章一定按照它们计算。

自定义 Taxonomy 的限制

如果自定义:

1
2
[taxonomies]
series = "series"

Hugo 可以正常生成 /series//series/<term>/,Stack 的通用 list.html 通常也可以渲染这些列表页面。但 Stack 的多个界面组件明确使用了 categoriestags

Stack 功能使用的 Taxonomy
文章分类徽章categories
文章标签组件tags
Category Widgetcategories
Tag Cloud Widgettags
归档页分类入口categories

因此,自定义 Taxonomy 可以被 Hugo 建模和生成页面,但不会自动获得 Stack 为默认 Category 和 Tag 提供的所有界面能力。需要显示徽章、Widget 或专用入口时,还要补充相应模板或组件。

一次完整的渲染过程

假设文章包含:

1
2
3
4
5
6
7
8
---
title: Hugo 页面模型
categories:
  - Hugo
tags:
  - Stack
  - Taxonomy
---

构建过程如下:

  1. Hugo 解析 categoriestags
  2. Hugo 把文章加入 categories/Hugotags/Stacktags/Taxonomy 三个 Term 集合。
  3. Hugo 创建或补全相应的 Taxonomy Page 和 Term Page。
  4. Stack 在文章页中通过 .GetTerms 渲染分类徽章和标签链接。
  5. Stack 的 Taxonomy Widget 从 .Site.Taxonomies 读取 Term 和文章数量。
  6. 用户进入 /tags/stack/ 后,Hugo 将该页面识别为 Kind=term
  7. Stack 的通用 list.html 遍历该 Term Page 的 .Pages 并渲染文章列表。

常见误区

误区实际情况
Stack 自己扫描文章并生成标签Hugo 建立 Taxonomy,Stack 只负责消费和展示
content/tags/_index.md 创建标签系统它只为已有 Taxonomy 根页面补充元信息
创建 Term 的 _index.md 就会关联文章文章仍需在 Front Matter 中声明对应 Term
content/page/tags/index.md 等同于标签根页前者是普通 Page,后者应是 Hugo Taxonomy Page
layout: tags 能把普通页变成 Taxonomylayout 只影响模板查找,不能改变 Page Kind
Category 和 Tag 在 Hugo 中能力不同二者机制相同,差异主要来自 Stack 的界面设计
自定义 Taxonomy 会自动显示在 Stack 各组件中通用列表可能可用,但主题组件通常只识别 categoriestags

组织原则

理解 Stack 中的 Taxonomy 时,应始终先分清数据模型和展示层:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
文章 Front Matter
    = 声明真实的分类与标签关系

content/categories、content/tags
    = 为 Hugo 自动生成的集合页面补充元信息

Hugo Taxonomy 系统
    = 建立关联、页面对象和 URL

Stack 模板
    = 决定这些数据如何显示

只要沿着这四层检查,就能判断问题究竟来自文章 Front Matter、Taxonomy 配置、内容路径,还是 Stack 模板匹配。

参考资料

使用 Hugo 构建
主题 StackJimmy 设计