免费获取学习方案
ARTICLE DETAIL

资讯详情

深耕编程基础知识与建站技术分享的一线实战洞察。

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

Hugo 菜单遍历方法全解析:ByName、ByWeight、Limit、Reverse 实战指南 Hugo 菜单遍历方法全解析ByName、ByWeight、Limit、Reverse 实战指南【免费下载链接】hugoThe world’s fastest framework for building websites.项目地址: https://gitcode.com/gh_mirrors/hu/hugoHugo 的Menu类型提供了一组用于遍历菜单条目menu entries的排序与截取方法。本文基于 Hugo 官方文档的 Menu methods 章节结合 navigation 包 的源码实现系统讲解ByName、ByWeight、Limit、Reverse四个方法的用途、排序规则、模板写法与源码原理读完即可在导航栏、页脚或面包屑等模板中熟练控制菜单条目的展示顺序与数量。方法与排序概览在 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以及可选的Pre、Post前后缀 HTML 与自定义Params具体定义见 MenuConfig 结构体。四个方法的签名与作用如下方法签名作用返回类型ByNameMENU.ByName按条目name排序navigation.MenuByWeightMENU.ByWeight按weight、再按name、再按identifier排序默认排序navigation.MenuLimitMENU.Limit N仅返回前 N 个条目navigation.MenuReverseMENU.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 }} lia href{{ .URL }}{{ .Name }}/a/li {{ end }} /ulHugo 渲染结果为ul lia href/about/About/a/li lia href/contactContact/a/li lia href/services/Services/a/li /ul从源码看ByName的实现通过比较m1.Name与m2.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 }} lia href{{ .URL }}{{ .Name }}/a/li {{ end }} /ul使用sort函数配合菜单条目时可以指定以下任一键Identifier、Name、Parent、Post、Pre、Title、URL或Weight。这些键对应 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在模板中按weight、name、identifier依次排序ul {{ range .Site.Menus.main.ByWeight }} lia href{{ .URL }}{{ .Name }}/a/li {{ end }} /ulHugo 渲染结果为ul lia href/services/Services/a/li lia href/about/About/a/li lia href/contactContact/a/li /ul[!NOTE] 在上面的菜单定义中identifier属性只有在两个或更多菜单条目具有相同name或需要使用翻译表本地化名称时才必须提供。源码中的默认排序比较器完整展现了先 weight、再 name、再 identifier的优先级见 navigation/menu.govar 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返回true、m1.Weight 0返回false的两个分支保证了这一点。因此若希望某个条目固定在列表最前应给它赋一个正权重而不设置 weight 的条目默认按 weight0 处理、排在末尾。ByWeight方法本身通过缓存调用此默认排序器见 navigation/menu.go。使用 sort 函数的替代方案同样可以使用sort函数按weight降序排列ul {{ range sort .Site.Menus.main Weight desc }} lia href{{ .URL }}{{ .Name }}/a/li {{ end }} /ul可指定的键与ByName一节相同Identifier、Name、Parent、Post、Pre、Title、URL或Weight。Limit截取前 N 个条目Limit方法返回给定的菜单仅保留前 N 个条目。它常与排序方法链式使用——先排序、再截取以得到权重最高的前 N 项之类的效果。仍以上一节的菜单定义Services/About/Contact为例先按名称排序、再只取前 2 项ul {{ range .Site.Menus.main.ByName.Limit 2 }} lia href{{ .URL }}{{ .Name }}/a/li {{ end }} /ulHugo 渲染结果为ul lia href/about/About/a/li lia href/contactContact/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方法返回反转排序顺序后的菜单通常与ByName、ByWeight等排序方法链式调用以获得降序效果。以同名菜单定义为例先按名称升序、再反转即得到按名称降序的结果ul {{ range .Site.Menus.main.ByName.Reverse }} lia href{{ .URL }}{{ .Name }}/a/li {{ end }} /ulHugo 渲染结果为ul lia href/services/Services/a/li lia href/contactContact/a/li lia 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 i1, 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 }} lia href{{ .URL }}{{ .Name }}/a/li {{ end }} /ul从源码可以观察到ByName、ByWeight、Reverse三个方法都通过smc.get(key, ...)获取结果smc是一个包级共享的菜单缓存实例var smc newMenuCache()见 navigation/menu.go。缓存实现在 navigation/menu_cache.go 中它以方法名如menuSort.ByName为键、以传入的原始菜单列表为匹配条件命中时直接返回已排序的结果未命中时才执行排序并写入缓存。这意味着在同一个页面构建周期内多次调用ByName/ByWeight/Reverse不会重复排序对大型站点的高频导航渲染是重要的性能保障。小结ByWeight是 Hugo 菜单的默认排序先weight再name最后identifierweight 为 0 的条目排末尾。ByName忽略 weight仅按名称排序需要降序时配合Reverse或sort函数。Limit N截取前 N 项常与排序方法链式使用Reverse整体反转当前顺序。使用sort函数时可指定的排序键为Identifier、Name、Parent、Post、Pre、Title、URL、Weight。四个方法的实现与默认排序器均位于 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),仅供参考
返回列表