
Hugo 模板函数 collections.Group 完全指南按 Key 分组页面集合与分页实战【免费下载链接】hugoThe world’s fastest framework for building websites.项目地址: https://gitcode.com/gh_mirrors/hu/hugo本指南深入讲解 Hugo 模板函数collections.Group别名group它如何将任意页面集合slice按自定义 Key 打包成page.PageGroup类型的分组对象以及如何与slice、first、last、.Paginate等组合实现按标签/新旧/任意维度分组的页面列表 分页的完整实战方案。读完你将掌握group的调用签名、PageGroup结构、与内置分组方法的异同以及可复制的分组分页模板代码。函数签名与定位collections.Group是 Hugo 模板层 collections 命名空间下的一个函数在模板中通过别名group调用。其文档定义的签名如下collections.Group KEY PAGESKEY任意类型的分组键在模板中通常传一个字符串如New、Old最终会成为分组对象PageGroup.Key字段的值PAGES要分组的页面集合slice例如.Site.RegularPages或first 10 .Site.RegularPages等管道链的输出返回值page.PageGroup即一个键 页面集合的结构体。对应源码位于 tpl/collections/collections.go注释明确说明Group groups a set of items by the given key且当前只支持 Pages// Group groups a set of items by the given key. // This is currently only supported for Pages. func (ns *Namespace) Group(key any, items any) (any, error) { if key nil { return nil, errors.New(nil is not a valid key to group by) } if g, ok : items.(collections.Grouper); ok { return g.Group(key, items) } in : newSliceElement(items) if g, ok : in.(collections.Grouper); ok { return g.Group(key, items) } return nil, fmt.Errorf(grouping not supported for type %T %T, items, in) }从源码可以看出三点实现细节nil不能作为 Keykey nil会直接返回错误nil is not a valid key to group by底层依赖Grouper接口collections.Group本身不实现具体分组逻辑而是将工作委托给实现了 common/collections/collections.go 中Grouper接口的类型该接口定义即Group(key any, items any) (any, error)。页面集合类型实现了该接口因此可以完成分组非页面类型不支持如果传入[]string、string等普通类型会返回grouping not supported for type ...错误——分组目前是 Pages 专属能力。单元测试佐证tpl/collections/collections_test.go 中的TestGroup用一组表格驱动用例验证了上述行为{a, []*tstGrouper{{}, {}}, a(2)}, {b, tstGroupers{tstGrouper{}, tstGrouper{}}, b(2)}, {a, []tstGrouper{{}, {}}, a(2)}, {a, []*tstGrouper{}, a(0)}, {a, []string{a, b}, false}, // 期望报错 {a, asdf, false}, // 期望报错 {a, nil, false}, // 期望报错 {nil, []*tstGrouper{{}, {}}, false}, // 期望报错可以看到只要类型实现了Grouper指针切片、值切片、命名切片均可就能按 Key 分组而[]string、字符串、nil输入或nilKey 都会失败。这与文档目前仅支持 Pages的说明完全一致。基本用法将任意页面集合打包为一个分组group最直接的用法是把一个页面集合按你指定的 Key 打包成一个PageGroup。文档给出的示例将站点常规页面切分为最新 10 篇与最早 10 篇两组{{ $new : .Site.RegularPages | first 10 | group New }} {{ $old : .Site.RegularPages | last 10 | group Old }} {{ $groups : slice $new $old }} {{ range $groups }} h3{{ .Key }}{{/* Prints New, Old */}}/h3 ul {{ range .Pages }} li a href{{ .RelPermalink }}{{ .LinkTitle }}/a div classmeta{{ .Date.Format Mon, Jan 2, 2006 }}/div /li {{ end }} /ul {{ end }}这段模板的关键链路是.Site.RegularPages | first 10先取出常规页面集合的前 10 篇得到一个新的页面 slice| group New用字符串New作为 Key把这 10 篇打包成一个PageGroupslice $new $old把两个PageGroup组装成一个分组列表PagesGrouprange $groups迭代每个分组用.Key访问分组名用.Pages遍历组内页面再通过.RelPermalink、.LinkTitle、.Date.Format渲染列表项。PageGroup 结构group返回的类型在 resources/page/pagegroup.go 中定义// PageGroup represents a group of pages, grouped by the key. // The key is typically a year or similar. type PageGroup struct { // The key, typically a year or similar. Key any // The Pages in this group. Pages }Key任意类型的键模板示例中为字符串New、OldPages内嵌的Pages类型因此.Pages直接就是一个完整的页面集合可以继续使用range、len、where等一切页面集合方法。多个PageGroup组成的分组列表类型为PagesGroup[]PageGroup见 resources/page/pagegroup.go它还提供Reverse()方法用于反转分组顺序以及Len()方法统计所有分组内页面总数。与内置 group 方法的关系同一类型双向打通文档特别强调group函数产生的PageGroup与 Hugo 内置 group 方法如GroupBy、GroupByDate、GroupByParam返回的分组是同一类型。内置分组方法定义在 resources/page/pagegroup.go 中例如Pages.GroupBy(ctx, key, order...)按页面的字段或方法值分组Pages.GroupByDate(format, order...)按日期字段分组Pages.GroupByParam(key, order...)按页面 Front Matter 参数分组此外还有GroupByPublishDate、GroupByExpiryDate、GroupByLastmod、GroupByParamDate等。它们返回的都是PagesGroup即[]PageGroup与slice $new $old组装出来的结构完全一致。这意味着用group手工打包的分组与用GroupByDate Jan 2006自动按月份生成的分组可以混用在同一套渲染逻辑中都通过.Key.Pages访问反过来内置分组方法得到的结果也直接兼容group相关的一切下游处理如分页、slice重组。这种函数与方法返回同构类型的设计让你可以在手工分组group与自动分组GroupBy*之间自由切换渲染模板无需改动。分组结果也可以分页文档指出The example above can be paginated上面的示例可以分页。由于group的产物是PageGroup/PagesGroup而 Hugo 的分页机制原生支持分组列表。Hugo 内置的.Paginate方法可直接接收PagesGroup并对分组进行分页官方分页文档 docs/content/en/templates/pagination.md 中的分组分页示例如下{{ $pages : where site.RegularPages Type posts }} {{ $paginator : .Paginate ($pages.GroupByDate Jan 2006) }} {{ range $paginator.PageGroups }} h2{{ .Key }}/h2 {{ range .Pages }} h3a href{{ .RelPermalink }}{{ .LinkTitle }}/a/h3 {{ end }} {{ end }} {{ partial pagination.html . }}将文档中的group示例同样接入分页可得到最新 10 篇 / 最早 10 篇 每页一个分组的组合方案{{ $new : .Site.RegularPages | first 10 | group New }} {{ $old : .Site.RegularPages | last 10 | group Old }} {{ $groups : slice $new $old }} {{ $paginator : .Paginate $groups }} {{ range $paginator.PageGroups }} h3{{ .Key }}/h3 ul {{ range .Pages }} li a href{{ .RelPermalink }}{{ .LinkTitle }}/a div classmeta{{ .Date.Format Mon, Jan 2, 2006 }}/div /li {{ end }} /ul {{ end }} {{ partial pagination.html . }}关于分页docs/content/en/templates/pagination.md 特别提示了最常见的坑同一个列表页不要多次调用分页。首次调用会被缓存且不可更改后续调用都会复用缓存结果导致行为与预期不符条件分页时应使用if-else而非compare.Conditional后者会急切求值两个分支。从源码看分页器内部通过ToPagesGroupresources/page/pagegroup.go把[]PageGroup、PagesGroup等输入统一转换为PagesGroup再进行分页如果传入的元素不是PageGroup会返回明确错误unsupported type in paginate from slice, got %T instead of PageGroup。这也印证了group的产物类型与分页管道是天然兼容的。典型应用场景基于上述能力group在真实站点中常用来实现以下几类需求最新/最旧区块如文档示例用first/last截取页面集合后打上自定义标签分组一次渲染两个区块自定义分类列表手工为任意切片如筛选后的结果集赋予语义化 Key再交给通用分组渲染模板分组分页将手工分组结果直接喂给.Paginate实现每页一个分组的归档页与GroupByDate等内置方法产出的分组共用同一套PageGroups渲染与分页逻辑与where、slice等集合函数串联group处于管道末端输入来自任何可产出Pages的表达式输出又能继续被slice、Paginate消费。常见错误与注意事项结合 tpl/collections/collections.go 的源码行为使用group时有以下边界需要留意场景结果nil作为 Key报错nil is not a valid key to group by传入非页面类型如[]string、字符串、数字切片报错grouping not supported for type ...传入nil页面集合报错同上空页面集合如first 0正常返回空分组len .Pages为 0页面集合Pages、*tstGrouper等实现Grouper的类型正常返回PageGroup文档 Front Matter 中标注的签名collections.Group KEY PAGES、返回类型page.PageGroup以及别名group历史别名/functions/group也都与实际源码一一对应可直接在模板中使用。小结collections.Groupgroup是 Hugo 模板中把任意页面集合打上自定义标签的轻量工具它返回与内置GroupBy*方法完全同构的page.PageGroup因此既可以独立渲染也能无缝接入slice重组与.Paginate分页管道。配合first/last/where等集合函数你可以在不引入任何分类体系的前提下自由构建最新与最旧标签分组分组归档页等页面区块。相关实现与测试可进一步参考 tpl/collections/collections.go、resources/page/pagegroup.go 与 tpl/collections/collections_integration_test.go。【免费下载链接】hugoThe world’s fastest framework for building websites.项目地址: https://gitcode.com/gh_mirrors/hu/hugo创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考