Gin分页需手动实现:后端计算TotalPages、CurrentPage、HasPrev/HasNext及PageNumbers切片(含省略逻辑),传入模板渲染;URL需保留非page参数,模板中用range配合字符串"..."和类型判断处理页码显示。

分页组件需要自己拼 HTML,Gin 没有内置分页函数
Gin 的 html/template 本身不提供分页逻辑或导航标签生成能力,Render 和 HTML 方法只负责渲染已准备好的数据。分页必须由你计算总页数、当前页、起始偏移等,并把结构化分页信息(如上一页/下一页链接、页码列表)传入模板。
常见错误是试图在模板里用 {{range}} 直接循环 1..TotalPages,但 Go 模板不支持数字区间迭代——你得提前生成一个 []int 页码切片传进去。
- 后端需计算:
TotalPages、CurrentPage、HasPrev、HasNext、PageNumbers(比如 [1,2,3,4,5] 或 [1,2,...,10,11]) - 避免把数据库查询和分页计算混在 handler 里;建议封装成
Pagination结构体,带Offset()和Limits()方法 - URL 参数名统一用
page,别用p或pageNum,方便前端和 SEO 一致处理
模板里用 range 渲染页码,但要小心空 slice 和边界
传入模板的 PageNumbers 是 []int,比如 [1 2 3 4 5] 或 [1 2 ... 9 10](注意省略号是字符串,不是数字)。模板中不能对 ... 做算术,所以推荐用字符串标识符(如 "...")+ 类型判断。
典型模板片段:
{{if .Pagination.HasPrev}}
<a href="https://www.php.cn/link/6e0344592d8bc716eb67771cd607b835">« 上一页</a>
{{else}}
<span class="disabled">« 上一页</span>
{{end}}
{{range .Pagination.PageNumbers}}
{{if eq . "..."}}
<span class="ellipsis">...</span>
{{else}}
{{if eq $.Pagination.CurrentPage .}}
<span class="current">{{.}}</span>
{{else}}
<a href="https://www.php.cn/link/edfb568a7756d77f0896c11da06983a8">{{.}}</a>
{{end}}
{{end}}
{{end}}
{{if .Pagination.HasNext}}
<a href="https://www.php.cn/link/395b197c65961b70132e7bea14250344">下一页 »</a>
{{else}}
<span class="disabled">下一页 »</span>
{{end}}
-
$.Pagination.CurrentPage中的$是根作用域,避免.CurrentPage在range内被覆盖 - 不要在模板里调用
len或做除法——Go 模板函数能力极弱,所有计算必须前置 - 如果
PageNumbers是空 slice,range会静默跳过,所以前后「上一页」「下一页」的显隐必须靠HasPrev/HasNext控制,不能依赖len(PageNumbers)
后端构造 PageNumbers 时要处理“省略”逻辑,别硬写 1~10
真实分页不会显示全部页码,比如总共 103 页,当前第 6 页,应显示 1 2 3 4 5 6 7 8 ... 102 103,而不是 1~103。这个逻辑必须在 Go 代码里完成,模板只负责呈现。
关键点:
- 固定显示首尾各 2 个页码:
1, 2, ..., Total-1, Total - 中间展示当前页 ±2 个:若
Current=6,中间段是4,5,6,7,8;若Current=2,则补足为1,2,3,4 - 用
map[string]interface{}或专用结构体传参,别用裸struct{}——字段名易错且无法加注释 - 注意整数除法和边界:当
TotalPages == 1时,HasPrev和HasNext都应为false,PageNumbers = []int{1}
URL 参数保留其他查询参数,否则筛选条件会丢失
点击分页链接时,如果只写 ?page=2,原本的 ?category=golang&sort=updated 就没了。必须把非 page 参数原样带上。
推荐做法:在 handler 里解析 r.URL.Query(),删掉 page 键,再拼回 query string:
q := r.URL.Query()
q.Del("page")
baseURL := r.URL.Path + "?" + q.Encode()
然后传 BaseURL 到模板,在链接里拼 {{.BaseURL}}&page=2。
- 别用
fmt.Sprintf("%s?page=%d", r.Referer(), page)——Referer 不可靠,可能为空或含 fragment - 如果用了 Gin 的
c.Request.URL,注意它不含 scheme 和 host,只用于相对路径 - 前端 JS 分页时也要同步处理 query,否则和服务端逻辑不一致


















