核心结论
Stack 没有重新实现 Taxonomy 系统。Hugo 负责读取文章 Front Matter、建立 Taxonomy 与 Term 的关联、生成页面对象和 URL;Stack 负责调用 Hugo 提供的 .Site.Taxonomies、.GetTerms 和 .Pages,将这些数据展示为分类徽章、文章标签、侧栏 Widget、归档入口以及列表页面。
可以把整体流程理解为:
| |
Hugo 默认的 Taxonomy
在没有显式配置 [taxonomies] 时,Hugo 默认启用 Category 和 Tag:
| |
左侧的 category 和 tag 是 Taxonomy 的 singular 名称,右侧的 categories 和 tags 是 plural 名称。文章 Front Matter 使用 plural 字段声明关联:
| |
Hugo 构建后会生成相应的页面对象和默认路径:
| 页面 | Page Kind | 含义 |
|---|---|---|
/categories/ | taxonomy | 所有 Category Term 的集合 |
/categories/hugo/ | term | 属于 Hugo Category 的文章集合 |
/tags/ | taxonomy | 所有 Tag Term 的集合 |
/tags/stack/ | term | 带有 Stack Tag 的文章集合 |
这些页面的数据来自文章 Front Matter,而不是来自 content/categories 或 content/tags 目录。
Hugo 与 Stack 的职责边界
| 能力 | Hugo | Stack |
|---|---|---|
解析文章中的 categories 和 tags | 负责 | 不负责 |
| 建立 Term 与文章的关联 | 负责 | 不负责 |
| 生成 Taxonomy 和 Term Page | 负责 | 不负责 |
| 生成默认 Taxonomy URL | 负责 | 不负责 |
| 选择最终模板 | 按 Template Lookup 执行 | 提供候选模板 |
| 展示分类徽章和标签 | 提供数据 | 负责 |
| 展示分类和标签 Widget | 提供 .Site.Taxonomies | 负责 |
| 设计分类颜色、卡片和布局 | 不负责 | 负责 |
因此,Stack 不会扫描文章并自行统计标签。它只是消费 Hugo 构建完成的内容模型。
Stack 如何渲染分类
Stack 在文章详情组件中通过下面的方式读取当前文章的分类:
| |
对应源码为:
| |
.GetTerms "categories" 返回的不是普通字符串,而是当前文章关联的 Category Term Page。Stack 可以直接读取每个 Term 的 .LinkTitle、.RelPermalink 和 .Params,将分类渲染成指向 /categories/<term>/ 的徽章。
分类徽章默认根据 Term 名称计算颜色。分类 Term 的 Front Matter 也可以通过 style.background 和 style.color 覆盖默认颜色:
| |
Stack 提供的 Category archetype 也包含这些字段:
| |
Stack 如何渲染标签
文章详情页通过下面的调用读取标签:
| |
对应源码为:
| |
Stack 通常把标签显示在文章正文后面。文章列表中的标签是可选内容,由 article.list.showTags 控制:
| |
Stack 对 Category 和 Tag 的定位并不完全相同。Category 更接近文章的主要归类,会显示为醒目的彩色徽章;Tag 更接近辅助检索信息,通常显示在文章底部或标签云中。这是主题的界面设计差异,不是 Hugo 数据模型上的能力差异。
Taxonomy 和 Term 页面如何选择模板
Stack 没有分别为 categories 和 tags 实现一套完整的专用列表模板。Hugo 按照 Template Lookup Order 查找模板后,通常会回退到 Stack 的通用列表模板:
| |
Stack 的页面筛选辅助模板明确处理 section、taxonomy 和 term:
| |
对应源码为:
| |
不同 Page Kind 进入同一套列表界面后,数据含义不同:
| |
Stack 的 list.html 再统一负责标题卡片、描述、封面、子项、文章列表和分页等界面。
content/tags 和 content/categories 的作用
推荐的可选元信息结构是:
| |
各文件的作用如下:
| 文件 | 补充元信息的页面 |
|---|---|
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 可以设置 title、description、image、菜单信息以及分类样式,但不会创建文章关联。即使不存在这些文件,只要 Taxonomy 已启用且文章声明了相应 Term,Hugo 仍能创建 Taxonomy 和 Term 页面。
反过来,仅创建下面的文件并不会自动让文章拥有 Stack 标签:
| |
文章仍然需要显式声明:
| |
为什么必须使用 _index.md
Taxonomy 根页面和 Term 页面都是集合节点,因此对应的元信息文件应使用 _index.md。例如:
| |
如果改成:
| |
这个目录会被声明为 Leaf Bundle,也就是普通内容 Page,可能与 Hugo 自动生成的 /tags/ Taxonomy Page 产生内容模型和模板查找冲突。
同理,把标签页放到下面的位置:
| |
创建的是 Kind=page、Type=page 的普通功能页面,不是 Hugo 的 Kind=taxonomy 页面。即使最终 URL 被 slug 或 permalink 改成 /tags/,它在内容模型中仍然不是 Tag Taxonomy 根节点。
layout: tags 也不能把普通 Page 转换成 Taxonomy。layout 只参与模板选择,页面的 Kind 仍由内容结构和 Hugo 的 Taxonomy 机制决定。
侧栏 Widget 如何读取 Taxonomy
当前站点启用了 Category 和 Tag Cloud Widget:
| |
Category Widget 和 Tag Cloud Widget 最终都会调用 Stack 的通用 Taxonomy Widget:
| |
核心读取逻辑是:
| |
然后通过 $taxonomy.ByCount 按文章数量排列 Term。因此,Widget 的统计数据来自 .Site.Taxonomies,并不依赖 content/tags/_index.md 或 content/categories/_index.md。
归档页对 Category 的特殊处理
Stack 的归档模板会额外读取 Category Taxonomy Page,并在归档页上展示分类入口:
| |
其核心逻辑会获取 categories 的 Taxonomy 根页面。Tag 没有进入同一段归档页逻辑,因此 Category 在 Stack 中比 Tag 多了一个归档入口。这仍然只是主题展示策略,不代表 Hugo 对 Category 和 Tag 的处理能力不同。
相关文章与 Taxonomy 的关系
Stack 的相关文章组件只是调用 Hugo 的 Related Content API:
| |
对应源码为:
| |
tags 或 categories 是否参与相似度计算、各自权重是多少,由 Hugo 的 Related Content 配置决定,不是 Stack 在模板中硬编码的行为。不能因为 Stack 展示了分类和标签,就直接推断相关文章一定按照它们计算。
自定义 Taxonomy 的限制
如果自定义:
| |
Hugo 可以正常生成 /series/ 和 /series/<term>/,Stack 的通用 list.html 通常也可以渲染这些列表页面。但 Stack 的多个界面组件明确使用了 categories 或 tags:
| Stack 功能 | 使用的 Taxonomy |
|---|---|
| 文章分类徽章 | categories |
| 文章标签组件 | tags |
| Category Widget | categories |
| Tag Cloud Widget | tags |
| 归档页分类入口 | categories |
因此,自定义 Taxonomy 可以被 Hugo 建模和生成页面,但不会自动获得 Stack 为默认 Category 和 Tag 提供的所有界面能力。需要显示徽章、Widget 或专用入口时,还要补充相应模板或组件。
一次完整的渲染过程
假设文章包含:
| |
构建过程如下:
- Hugo 解析
categories和tags。 - Hugo 把文章加入
categories/Hugo、tags/Stack和tags/Taxonomy三个 Term 集合。 - Hugo 创建或补全相应的 Taxonomy Page 和 Term Page。
- Stack 在文章页中通过
.GetTerms渲染分类徽章和标签链接。 - Stack 的 Taxonomy Widget 从
.Site.Taxonomies读取 Term 和文章数量。 - 用户进入
/tags/stack/后,Hugo 将该页面识别为Kind=term。 - 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 能把普通页变成 Taxonomy | layout 只影响模板查找,不能改变 Page Kind |
| Category 和 Tag 在 Hugo 中能力不同 | 二者机制相同,差异主要来自 Stack 的界面设计 |
| 自定义 Taxonomy 会自动显示在 Stack 各组件中 | 通用列表可能可用,但主题组件通常只识别 categories 和 tags |
组织原则
理解 Stack 中的 Taxonomy 时,应始终先分清数据模型和展示层:
| |
只要沿着这四层检查,就能判断问题究竟来自文章 Front Matter、Taxonomy 配置、内容路径,还是 Stack 模板匹配。