分页
把列表页拆分为多个分页,并生成页码导航。
为什么需要分页
在列表页上一次性展示大量页面集合并不友好:
- 超长列表令人生畏且难以浏览,访客容易在海量信息中迷失;
- 页面越大加载越慢,可能让人失去耐心而离开站点;
- 没有任何筛选或组织时,找到一个特定条目变成了漫长的滚动。
对 home、section、taxonomy、term 这几类列表页进行分页可以改善可用性。
术语
- paginate
- 把一个列表页拆分为两个或多个子集。
- pagination
- 对列表页进行分页的过程。
- pager
- 分页过程中产生的分页器,包含列表页的一个子集以及指向其他分页的导航链接。
- paginator
- 一组 pager 的集合。
配置
分页的默认行为由项目配置中的 [pagination] 小节决定:
pagerSize:每个 pager 中包含的页面数量;path:分页路径的片段,默认是page;disableAliases:是否禁用第一个 pager 的别名,默认是false。
方法
要对 home、section、taxonomy 或 term 页面分页,在对应模板的 Page 对象上调用以下方法之一:
.Paginate.Paginator
.Paginate 更灵活,它可以:
- 对任意页面集合分页;
- 对页面集合进行筛选、排序和分组;
- 覆盖项目配置中定义的每页数量。
相比之下,.Paginator 只对传入模板的那个页面集合分页,并且不能覆盖每页数量,始终使用 pagination.pagerSize 配置值。
使用 .Paginate
{{ $pages := where site.RegularPages "Type" "posts" }}
{{ $paginator := .Paginate $pages.ByTitle 7 }}
{{ range $paginator.Pages }}
<h2><a href="{{ .RelPermalink }}">{{ .LinkTitle }}</a></h2>
{{ end }}
{{ partial "pagination.html" . }}上面依次做了五件事:
- 构建页面集合;
- 按标题排序;
- 对该集合分页,每个 pager 放 7 页;
- 遍历分页后的集合,为每页渲染一个链接;
- 调用内建的分页模板,生成 pager 之间的导航链接。
使用 .Paginator
{{ range .Paginator.Pages }}
<h2><a href="{{ .RelPermalink }}">{{ .LinkTitle }}</a></h2>
{{ end }}
{{ partial "pagination.html" . }}这里的三步是:
- 对传入模板的页面集合分页,每页数量取默认配置;
- 遍历分页后的集合,为每页渲染一个链接;
- 调用内建的分页模板生成导航链接。
缓存
无论用哪种方法,首次调用会被缓存且不可更改。如果在同一个列表页上多次调用分页,后续调用使用的都是缓存结果,也就是说后续调用不会按代码字面意思生效。
需要按条件分页时,不要使用 compare.Conditional 函数,因为它会急切求值所有参数;改用 if-else 结构来控制调用时机。
分组
分页可以与任意分组方法配合使用:
{{ $pages := where site.RegularPages "Type" "posts" }}
{{ $paginator := .Paginate ($pages.GroupByDate "Jan 2006") }}
{{ range $paginator.PageGroups }}
<h2>{{ .Key }}</h2>
{{ range .Pages }}
<h3><a href="{{ .RelPermalink }}">{{ .LinkTitle }}</a></h3>
{{ end }}
{{ end }}
{{ partial "pagination.html" . }}分组之后通过 $paginator.PageGroups 遍历各个分组,每个分组用 .Key 表示分组键、用 .Pages 表示组内页面。
导航
如前面的示例所示,在 pager 之间添加导航最简单的办法是使用 Hugo 内建的分页模板:
{{ partial "pagination.html" . }}内建的分页模板有两种格式:default 和 terse。上面的写法等价于:
{{ partial "pagination.html" (dict "page" . "format" "default") }}terse 格式的控件和页码槽位更少,渲染成横向列表时占用更少空间:
{{ partial "pagination.html" (dict "page" . "format" "terse") }}需要自定义导航组件时,可以使用 pager 对象提供的各种方法读取上一页、下一页、首页、末页等信息,自行拼装链接。
发布结构
下面的例子展示列表页分页后,发布到站点的目录结构。假设内容如下:
content/
├── posts/
│ ├── _index.md
│ ├── post-1.md
│ ├── post-2.md
│ ├── post-3.md
│ └── post-4.md
└── _index.md项目配置如下:
[pagination]
disableAliases = false
pagerSize = 2
path = 'page'section 模板如下:
{{ range (.Paginate .Pages).Pages }}
<h2><a href="{{ .RelPermalink }}">{{ .LinkTitle }}</a></h2>
{{ end }}
{{ partial "pagination.html" . }}发布后的站点结构是:
public/
├── posts/
│ ├── page/
│ │ ├── 1/
│ │ │ └── index.html <-- 指向 public/posts/index.html 的别名
│ │ └── 2/
│ │ └── index.html
│ ├── post-1/
│ │ └── index.html
│ ├── post-2/
│ │ └── index.html
│ ├── post-3/
│ │ └── index.html
│ ├── post-4/
│ │ └── index.html
│ └── index.html
└── index.html要禁止为第一个 pager 生成别名,修改项目配置:
[pagination]
disableAliases = true
pagerSize = 2
path = 'page'此时发布结构变为:
public/
├── posts/
│ ├── page/
│ │ └── 2/
│ │ └── index.html
│ ├── post-1/
│ │ └── index.html
│ ├── post-2/
│ │ └── index.html
│ ├── post-3/
│ │ └── index.html
│ ├── post-4/
│ │ └── index.html
│ └── index.html
└── index.html区别只在于 posts/page/1/ 这个别名目录:第一个 pager 的内容与列表页本身相同,别名让旧链接继续可用;如果托管平台或链接策略不需要它,关掉即可少生成一个目录。