Hugo 有序分类法(Ordered Taxonomy)元素方法详解:Count、Page、Pages、Term 与 WeightedPages
【免费下载链接】hugoThe world’s fastest framework for building websites.项目地址: https://gitcode.com/gh_mirrors/hu/hugo
Hugo 中分类法(Taxonomy)对象本质是一个 Go 的 map,而 map 无法保持顺序,因此 Hugo 提供了Alphabetical和ByCount两个方法将其转换为"有序分类法"——一个每个元素包含术语(term)及其加权页面(weighted pages)切片的有序列表。读完本文,你将掌握有序分类法五个元素方法(Count、Page、Pages、Term、WeightedPages)的返回类型、用途与取舍,并理解它们在 resources/page/taxonomy.go 与 resources/page/weighted.go 中的底层实现。
什么是有序分类法
在 Hugo 的数据模型中,原始Taxonomy类型定义为一个 map,如 resources/page/taxonomy.go 所示:
type Taxonomy map[string]WeightedPagesmap 在遍历时顺序不确定,因此在模板中遍历分类法前需要先排序。调用Taxonomy对象的Alphabetical(按键名排序)或ByCount(按页面数排序)方法后,得到的有序分类法(ordered taxonomy)是一个 slice,其中每个元素是一个对象,包含该术语及其加权页面切片。这一概念也与 术语表中的定义 一致:有序分类法即"一个 slice,每个元素是一个包含 term 和其加权页面切片的对象"。
从源码结构看,这个"元素"对应OrderedTaxonomyEntry结构体(见 resources/page/taxonomy.go):
// OrderedTaxonomyEntry is similar to an element of a Taxonomy, but with the key embedded (as name) // e.g: {Name: Technology, WeightedPages: TaxonomyPages} type OrderedTaxonomyEntry struct { Name string WeightedPages }它把 map 的键以Name字段内嵌,同时组合了WeightedPages切片——文档中描述的"包含 term 和加权页面切片的对象"正是这个结构。
元素方法一览
有序分类法切片中的每个元素提供以下五个方法(完整文档见 ordered-taxonomy-element-methods.md):
Count:该术语下的页面数
- 返回类型:
int - 用途:返回分配到该术语的页面数量,常用于显示"(3 篇文章)"这类计数标签。
源码实现(resources/page/taxonomy.go):
// Count returns the count the pages in this taxonomy. func (ie OrderedTaxonomyEntry) Count() int { return len(ie.WeightedPages) }可见它就是对内嵌WeightedPages切片取长度,开销极低。
Page:术语页本身
- 返回类型:
page.Page - 用途:返回该术语对应的
Page对象,最典型的用途是链接到术语页。
该方法继承自内嵌的WeightedPages类型(见 resources/page/weighted.go):
// Page will return the Page (of Kind taxonomyList) that represents this set // of pages. This method will panic if p is empty, as that should never happen. func (p WeightedPages) Page() Page { ... return first.owner }WeightedPage结构体中持有一个未导出的owner字段(指向所属的 taxonomy 页面),它避免了每次调用都要做.Site.GetPage查询。从源码注释看,这个间接引用方式是为了"不破坏现有模板"而采用的折中设计。
Pages:按分类法权重排序的 Pages 对象
- 返回类型:
page.Pages - 用途:返回包含分配到该术语的
Page对象的Pages对象,且按分类法权重(taxonomic weight)排序。
实现(resources/page/taxonomy.go)只是对WeightedPages.Pages()的转发:
// Pages returns the Pages for this taxonomy. func (ie OrderedTaxonomyEntry) Pages() Pages { return ie.WeightedPages.Pages() }关键点在于Pages对象支持Sort、GroupBy、Where等 Pages 方法,因此比WeightedPages更灵活。例如,你可以按最后修改日期重新排序,而不是沿用默认的权重顺序。
Term:术语名称
- 返回类型:
string - 用途:返回术语名(即 map 键),实现见 resources/page/taxonomy.go:
// Term returns the name given to this taxonomy. func (ie OrderedTaxonomyEntry) Term() string { return ie.Name }WeightedPages:按权重排序的加权页面切片
- 返回类型:
page.WeightedPages - 用途:返回分配到该术语的加权页面切片,按分类法权重排序。文档同时指出,上面的
Pages方法更灵活,可以进一步排序和分组,因此需要灵活处理时优先用Pages。
WeightedPages的定义与排序规则在 resources/page/weighted.go:
// WeightedPages is a list of Pages with their corresponding (and relative) weight // [{Weight: 30, Page: *1}, {Weight: 40, Page: *2}] type WeightedPages []WeightedPages其比较逻辑(weighted.go)说明了"按权重排序"的确切含义:
func (wp WeightedPages) Less(i, j int) bool { if wp[i].Weight == wp[j].Weight { return DefaultPageSort(wp[i].Page, wp[j].Page) } return wp[i].Weight < wp[j].Weight }即先按权重值比较,权重相同时回退到 Hugo 的默认页面排序。每个元素是WeightedPage结构体——一个"带权重的 Page"(weighted.go)。
模板实战:遍历有序分类法
以下示例继承自 get-a-taxonomy-object.md 的完整工作场景:项目配置了两个分类法,内容目录带有genres前端的书籍页面:
[taxonomies] genre = 'genres' author = 'authors'content/ ├── books/ │ ├── and-then-there-were-none.md --> genres: suspense │ ├── death-on-the-nile.md --> genres: suspense │ └── jamaica-inn.md --> genres: suspense, romance │ └── pride-and-prejudice.md --> genres: romance └── _index.md在任意模板中先通过Site对象的Taxonomies方法捕获Taxonomy对象,再调用Alphabetical/ByCount得到有序分类法,然后 range 遍历其元素并调用本文介绍的五个方法:
{{ $taxonomyObject := .Site.Taxonomies.genres.ByCount }} {{ range $taxonomyObject }} <h2><a href="{{ .Page.RelPermalink }}">{{ .Term }} ({{ .Count }})</a></h2> <ul> {{ range .Pages }} <li><a href="{{ .RelPermalink }}">{{ .LinkTitle }}</a></li> {{ end }} </ul> {{ end }}在 taxonomy 模板内部(如layouts/taxonomy.html),等价写法是用页面Data对象的Terms方法获取对象:
{{ $taxonomyObject := .Data.Terms }}调试数据结构的通用手段:
<pre>{{ debug.Dump $taxonomyObject }}</pre>如果不经过Alphabetical/ByCount,也可以直接 rangeTaxonomymap(键为术语、值为加权页面切片)渲染加权页面,此时元素上同样可用.Page、.Term等;但顺序是不确定的,生产模板中推荐显式排序。
排序机制的源码佐证
ByCount的排序规则值得注意:先按每键页面数降序,数量相同时按键名升序(resources/page/taxonomy.go):
// ByCount returns an ordered taxonomy sorted by # of pages per key. // If taxonomies have the same # of pages, sort them alphabetical func (i Taxonomy) ByCount() OrderedTaxonomy { count := func(i1, i2 *OrderedTaxonomyEntry) bool { li1 := len(i1.WeightedPages) li2 := len(i2.WeightedPages) if li1 == li2 { return compare.LessStrings(i1.Name, i2.Name) } return li1 > li2 } ... }Alphabetical则使用语言感知的排序器(langs.GetCollator1),对不同语言环境下的术语比较做了本地化处理(taxonomy.go)。两种排序最终都通过sort.Stable稳定排序执行(taxonomy.go),保证相等元素之间的顺序稳定。
集成测试也验证了这些行为,例如 hugolib/taxonomy_test.go 中的TestTaxonomiesCountOrder直接 range.ByCount()结果检查计数顺序,TestTaxonomiesWeightSort(taxonomy_test.go)验证加权页面排序,TestTaxonomiesSpaceInName则在模板中 range.Data.Terms.ByCount检验含空格术语名的场景。
参考与延伸阅读
- 元素方法原始文档:ordered-taxonomy-element-methods.md
- 获取 Taxonomy 对象:get-a-taxonomy-object.md
- 术语表定义:ordered-taxonomy.md
Alphabetical方法文档:Alphabetical.mdByCount方法文档:ByCount.md- 核心实现:resources/page/taxonomy.go、resources/page/weighted.go
- 集成测试:hugolib/taxonomy_test.go
【免费下载链接】hugoThe world’s fastest framework for building websites.项目地址: https://gitcode.com/gh_mirrors/hu/hugo
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考