Hugo 菜单遍历方法全解析:ByName、ByWeight、Limit、Reverse 实战指南
2026/9/19 16:38:25 网站建设 项目流程

Hugo 菜单遍历方法全解析:ByName、ByWeight、Limit、Reverse 实战指南

【免费下载链接】hugoThe world’s fastest framework for building websites.项目地址: https://gitcode.com/gh_mirrors/hu/hugo

Hugo 的Menu类型提供了一组用于遍历菜单条目(menu entries)的排序与截取方法。本文基于 Hugo 官方文档的 Menu methods 章节,结合 navigation 包 的源码实现,系统讲解ByNameByWeightLimitReverse四个方法的用途、排序规则、模板写法与源码原理,读完即可在导航栏、页脚或面包屑等模板中熟练控制菜单条目的展示顺序与数量。

方法与排序概览

在 Hugo 中,Menu是一个菜单条目的集合,定义于 navigation/menu.go:

// Menu is a collection of menu entries. type Menu []*MenuEntry // Menus is a dictionary of menus. type Menus map[string]Menu

每个MenuEntry包含标识符(Identifier)、名称(Name)、父级(Parent)、权重(Weight)、链接地址(URL/PageRef)、标题(Title)以及可选的PrePost前后缀 HTML 与自定义Params,具体定义见 MenuConfig 结构体。

四个方法的签名与作用如下:

方法签名作用返回类型
ByNameMENU.ByName按条目name排序navigation.Menu
ByWeightMENU.ByWeightweight、再按name、再按identifier排序(默认排序)navigation.Menu
LimitMENU.Limit N仅返回前 N 个条目navigation.Menu
ReverseMENU.Reverse反转条目的当前排序顺序navigation.Menu

这些方法不修改原始菜单,而是返回一个新的有序(或截取后的)菜单副本,因此可以在range中安全地链式调用,例如.Site.Menus.main.ByName.Limit 2

ByName:按名称排序

ByName方法返回按name排序的菜单条目。考虑如下菜单定义:

# hugo.toml [[menus.main]] name = 'Services' pageRef = '/services' weight = 10 [[menus.main]] name = 'About' pageRef = '/about' weight = 20 [[menus.main]] name = 'Contact' pageRef = '/contact' weight = 30

在模板中按name排序遍历:

<ul> {{ range .Site.Menus.main.ByName }} <li><a href="{{ .URL }}">{{ .Name }}</a></li> {{ end }} </ul>

Hugo 渲染结果为:

<ul> <li><a href="/about/">About</a></li> <li><a href="/contact">Contact</a></li> <li><a href="/services/">Services</a></li> </ul>

从源码看,ByName的实现通过比较m1.Namem2.Name来决定顺序(使用compare.LessStrings),见 navigation/menu.go:

// ByName sorts the menu by the name defined in the menu configuration. func (m Menu) ByName() Menu { const key = "menuSort.ByName" title := func(m1, m2 *MenuEntry) bool { return compare.LessStrings(m1.Name, m2.Name) } menus, _ := smc.get(key, menuEntryBy(title).Sort, m) return menus }

使用 sort 函数的替代方案

你也可以使用sort函数 来排序菜单条目。例如按name降序排列:

<ul> {{ range sort .Site.Menus.main "Name" "desc" }} <li><a href="{{ .URL }}">{{ .Name }}</a></li> {{ end }} </ul>

使用sort函数配合菜单条目时,可以指定以下任一键:IdentifierNameParentPostPreTitleURLWeight。这些键对应 MenuConfig 中的字段名。

ByWeight:默认排序规则

ByWeight方法返回按weight、再按name、再按identifier排序的菜单条目。这是 Hugo 菜单的默认排序顺序——即使不显式调用任何排序方法,Hugo 也会按此规则渲染菜单。

考虑如下带identifier的菜单定义:

# hugo.toml [[menus.main]] identifier = 'about' name = 'About' pageRef = '/about' weight = 20 [[menus.main]] identifier = 'services' name = 'Services' pageRef = '/services' weight = 10 [[menus.main]] identifier = 'contact' name = 'Contact' pageRef = '/contact' weight = 30

在模板中按weightnameidentifier依次排序:

<ul> {{ range .Site.Menus.main.ByWeight }} <li><a href="{{ .URL }}">{{ .Name }}</a></li> {{ end }} </ul>

Hugo 渲染结果为:

<ul> <li><a href="/services/">Services</a></li> <li><a href="/about/">About</a></li> <li><a href="/contact">Contact</a></li> </ul>

[!NOTE] 在上面的菜单定义中,identifier属性只有在两个或更多菜单条目具有相同name,或需要使用翻译表本地化名称时才必须提供。

源码中的默认排序比较器完整展现了"先 weight、再 name、再 identifier"的优先级,见 navigation/menu.go:

var defaultMenuEntrySort = func(m1, m2 *MenuEntry) bool { if m1.Weight == m2.Weight { c := compare.Strings(m1.Name, m2.Name) if c == 0 { return m1.Identifier < m2.Identifier } return c < 0 } if m2.Weight == 0 { return true } if m1.Weight == 0 { return false } return m1.Weight < m2.Weight }

一个容易忽略的细节是:weight 为 0 的条目会被排到所有带 weight 的条目之后(源码中m2.Weight == 0返回truem1.Weight == 0返回false的两个分支保证了这一点)。因此若希望某个条目固定在列表最前,应给它赋一个正权重;而不设置 weight 的条目默认按 weight=0 处理、排在末尾。ByWeight方法本身通过缓存调用此默认排序器,见 navigation/menu.go。

使用 sort 函数的替代方案

同样可以使用sort函数按weight降序排列:

<ul> {{ range sort .Site.Menus.main "Weight" "desc" }} <li><a href="{{ .URL }}">{{ .Name }}</a></li> {{ end }} </ul>

可指定的键与ByName一节相同:IdentifierNameParentPostPreTitleURLWeight

Limit:截取前 N 个条目

Limit方法返回给定的菜单,仅保留前 N 个条目。它常与排序方法链式使用——先排序、再截取,以得到"权重最高的前 N 项"之类的效果。

仍以上一节的菜单定义(Services/About/Contact)为例,先按名称排序、再只取前 2 项:

<ul> {{ range .Site.Menus.main.ByName.Limit 2 }} <li><a href="{{ .URL }}">{{ .Name }}</a></li> {{ end }} </ul>

Hugo 渲染结果为:

<ul> <li><a href="/about/">About</a></li> <li><a href="/contact">Contact</a></li> </ul>

源码实现非常直接,见 navigation/menu.go:

// Limit limits the returned menu to n entries. func (m Menu) Limit(n int) Menu { if len(m) > n { return m[0:n] } return m }

当菜单条目数不超过 N 时,Limit原样返回整个菜单,不会报错也不会补空;当条目数超过 N 时,通过切片m[0:n]截取前 N 项。实际项目中可用它实现"只显示最新/最靠前的 5 个导航链接"等场景。

Reverse:反转排序顺序

Reverse方法返回反转排序顺序后的菜单,通常与ByNameByWeight等排序方法链式调用,以获得降序效果。

以同名菜单定义为例,先按名称升序、再反转,即得到按名称降序的结果:

<ul> {{ range .Site.Menus.main.ByName.Reverse }} <li><a href="{{ .URL }}">{{ .Name }}</a></li> {{ end }} </ul>

Hugo 渲染结果为:

<ul> <li><a href="/services/">Services</a></li> <li><a href="/contact">Contact</a></li> <li><a href="/about/">About</a></li> </ul>

源码实现采用经典的双指针原地交换,见 navigation/menu.go:

// Reverse reverses the order of the menu entries. func (m Menu) Reverse() Menu { const key = "menuSort.Reverse" reverseFunc := func(menu Menu) { for i, j := 0, len(menu)-1; i < j; i, j = i+1, j-1 { menu[i], menu[j] = menu[j], menu[i] } } menus, _ := smc.get(key, reverseFunc, m) return menus }

Reverse是对当前顺序的整体反转,而非"按某个字段降序排序"。因此它的语义取决于前置排序:ByName.Reverse是名称降序,ByWeight.Reverse则是 weight 降序。若菜单尚未排序,Reverse反转的就是配置中的原始声明顺序。

链式调用与缓存机制

四个方法均可任意组合,形成"排序 → 截取 → 反转"的链式调用,例如:

<!-- 按 weight 升序取前 3 项后再反转(实际得到 weight 较大的前 3 项) --> <ul> {{ range .Site.Menus.main.ByWeight.Limit 3.Reverse }} <li><a href="{{ .URL }}">{{ .Name }}</a></li> {{ end }} </ul>

从源码可以观察到,ByNameByWeightReverse三个方法都通过smc.get(key, ...)获取结果,smc是一个包级共享的菜单缓存实例(var smc = newMenuCache(),见 navigation/menu.go)。缓存实现在 navigation/menu_cache.go 中:它以方法名(如"menuSort.ByName")为键、以传入的原始菜单列表为匹配条件,命中时直接返回已排序的结果,未命中时才执行排序并写入缓存。这意味着在同一个页面构建周期内多次调用ByName/ByWeight/Reverse不会重复排序,对大型站点的高频导航渲染是重要的性能保障。

小结

  • ByWeight是 Hugo 菜单的默认排序:先weight,再name,最后identifier;weight 为 0 的条目排末尾。
  • ByName忽略 weight,仅按名称排序;需要降序时配合Reversesort函数。
  • Limit N截取前 N 项,常与排序方法链式使用;Reverse整体反转当前顺序。
  • 使用sort函数时,可指定的排序键为:IdentifierNameParentPostPreTitleURLWeight
  • 四个方法的实现与默认排序器均位于 navigation/menu.go,排序结果缓存逻辑见 navigation/menu_cache.go,相关行为可参考 navigation 包测试。

掌握这四个方法,即可在 Hugo 模板中精确控制导航菜单的显示顺序与数量,无需引入任何额外依赖或自定义排序逻辑。

【免费下载链接】hugoThe world’s fastest framework for building websites.项目地址: https://gitcode.com/gh_mirrors/hu/hugo

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询