内容管理

菜单

在配置或前置元数据中定义菜单项,并在模板中遍历与高亮。

菜单系统的三个环节

菜单(menu)由一组菜单项(menu entry)组成,每一项最终渲染为一个链接。为站点建立菜单需要三步:定义菜单项、为每种语言本地化菜单项、在模板中渲染。站点可以有多个菜单,既可以平铺也可以嵌套,例如页头放一个主菜单,页脚再放一个独立菜单。

定义菜单项有三种方式:自动生成、写在前置元数据(front matter)中、写在项目配置中。三种方式可以混用,但整站统一使用一种方式会让菜单更容易理解和维护。

自动生成

在项目配置中开启 section 页面菜单,Hugo 就会为站点的每个一级 section(内容区块)自动生成一个菜单项:

sectionPagesMenu = 'main'

生成的菜单结构在模板中通过 site.Menus.main 访问。

在前置元数据中定义

把页面加入 main 菜单:

title = '关于'
menus = 'main'

让页面同时出现在 main 与 footer 菜单中:

title = '联系'
menus = ['main', 'footer']

在前置元数据中定义菜单项时可以使用的属性:

属性 说明
identifier 菜单项标识。多个菜单项同名,或用翻译表本地化 name 时必须提供;必须以字母开头,后接字母、数字或下划线
name 渲染菜单项时显示的文字
params 自定义的菜单项参数映射
parent 父菜单项的 identifier;父项未定义 identifier 时改用其 name,嵌套菜单中的子项必须提供
post 渲染菜单项时追加在其后的 HTML
pre 渲染菜单项时前置的 HTML
title 渲染结果的 HTML title 属性
weight 非零整数,决定菜单项相对菜单根(子项则相对其父项)的位置,数值越小越靠前

一个用到部分属性的前置元数据示例如下:

title = '软件'
[menus.main]
  parent = '产品'
  weight = 20
  pre = '<i class="fa-solid fa-code"></i>'
  [menus.main.params]
    class = 'center'

在项目配置中定义

导航栏这类需要集中管理的菜单,直接写在项目配置里:

[[menus.main]]
  name = '首页'
  pageRef = '/'
  weight = 10

[[menus.main]]
  name = '产品'
  pageRef = '/products'
  weight = 20

[[menus.main]]
  name = '服务'
  pageRef = '/services'
  weight = 30

页脚菜单写法相同,把 [[menus.main]] 换成 [[menus.footer]],再用 site.Menus.footer 访问。

菜单项通常至少包含 name、weight,以及 pageRef 或 url 之一:指向站内页面用 pageRef,指向站外地址用 url。pageRef 接受目标页面的逻辑路径:

页面种类 pageRef
home /
page /books/book-1
section /books
taxonomy /tags
term /tags/foo

嵌套菜单

把子项的 parent 指向父项,就形成嵌套菜单:

[[menus.main]]
  name = '产品'
  pageRef = '/products'
  weight = 10

[[menus.main]]
  name = '硬件'
  pageRef = '/products/hardware'
  parent = '产品'
  weight = 1

[[menus.main]]
  name = '软件'
  pageRef = '/products/software'
  parent = '产品'
  weight = 2

[[menus.main]]
  name = 'Hugo'
  pre = '<i class="fa fa-heart"></i>'
  url = 'https://gohugo.io/'
  weight = 30
  [menus.main.params]
    rel = 'external'

在模板中渲染

菜单数据统一通过 site.Menus.<菜单名> 访问:

调用菜单方法时需要当前页面的上下文,因此先在模板开头保存页面对象:

{{ $currentPage := . }}
<nav>
  <ul>
    {{ range site.Menus.main }}
      <li>
        <a href="{{ .URL }}"{{ if $currentPage.IsMenuCurrent .Menu . }} class="active"{{ end }}>
          {{ .Name }}
        </a>
        {{ with .Children }}
          <ul>
            {{ range . }}
              <li><a href="{{ .URL }}">{{ .Name }}</a></li>
            {{ end }}
          </ul>
        {{ end }}
      </li>
    {{ end }}
  </ul>
</nav>

每个菜单项提供 .URL、.Name、.Identifier、.Page、.Params 等属性,.HasChildren 与 .Children 用于渲染多级菜单。IsMenuCurrent 与 HasMenuCurrent 是 Page 对象上的方法,第一个参数是菜单项所属的 Menu 对象,也就是条目自身的 .Menu,第二个参数是菜单项本身,因此写成 $currentPage.IsMenuCurrent .Menu .:前者判断当前页面是否就是该菜单项,后者判断当前页面是否位于该菜单项的子层级中,常用于高亮当前栏目及其祖先项。使用这两个方法时,菜单项必须写在前置元数据中,或者在项目配置里为它指定 pageRef。

本地化

多语言站点中,菜单项通常需要按语言分别定义,或者用翻译表本地化 name,具体做法见 多语言。

延伸阅读