后台列表翻到第 3 页,链接变成 article?id=3,点进去看到的却是 id 为 3 的那篇文章。这种事故不稀奇:同一个参数名,一半代码当记录主键用,一半当页码用,谁后写谁生效。

id 是资源定位,页码是视图状态

id 指向一条具体记录,页码描述列表切到第几屏。两者的取值范围、缓存策略、能不能被收录,全都不一样。混成一个参数,翻页缓存会把不同页的 HTML 串在一起,埋点也会把详情页访问算成列表翻页。

URL 上怎么分开

列表用 page,详情用 id,路径也拆开:/article/12 是详情,/article/list?page=3 是列表。页码从 1 开始,别一页从 0 一页从 1,前端组件和后端 offset 对不上就会重复或漏数据。offset 等于页码减一再乘每页条数,这条公式写进注释,比事后对账省事。

参数得兜底,别信前端传的值

page 传 0、传负数、传个 999999,后端都要接住。小于 1 按 1 处理,超过总页数返回空列表或者直接跳末页,别让 offset 算成负数再丢给 SQL。总数查询和列表查询要用同一套过滤条件,否则最后一页永远是空的。页码还要跟筛选条件一起绑定,切了分类就回到第一页,排序变了也一样重置,不然用户点进去看到的是一页空表,还以为数据没了。

数据库分页别拿 id 当页码

有人图省事,直接 where id > 上一页末尾的 id。这叫游标分页,适合按 id 递增的流水表。列表一旦改成按时间或热度排序,id 不再单调,跳页就会丢数据。翻到深页 offset 很大确实慢,那就把排序字段和 id 做成联合游标,别硬套单列 id。

前端两个 id 也容易撞

分页按钮的 id 写成 page-1、page-2 没问题,但把记录 id 和页码塞进同一个属性,事件委托里取到的值就可能错位。用 data-page 和 data-id 分开存,取值时各拿各的,调试时一眼能看出谁是谁。

取舍很清楚:要分享某一页、要被搜索引擎收录,就用 page 参数;纯接口内部翻数据,游标更稳。唯一不能做的,是让 id 一个字段干两份活。