在一个列表中,如果避免一次请求大量数据的问题,使用分页是最好的解决方式,而且在接触开发以来,也一直认为分页是最简单的基础,最容易实现的部分。然后在正式工作的第一个月,便改变了这个思想,虽然分页还是最基础的部分,但是已经不认为它是简单的了,它的实现,在某些场景下,让现在的我觉得还有很棘手的。
本文又名《简单的分页不简单》。为本人的第一篇工作笔记,以下所有的内容,均仅作为个人学习总结所用,特此声明!本文记录下当前这个状态的自己对于分页的理解,会留下目前解决不了的问题,等待未来的自己能够回来替自己解答。
参考文章:
https://developer.twitter.com/en/docs/tweets/timelines/guides/working-with-timelines
http://zhiheng.me/156
https://aotu.io/notes/2017/06/27/infinite-scrolling/index.html
电梯式
”电梯式分页“,我觉得这个名字是十分贴切,这个名词形象的描述了这种分页模式的过程。常见的有百度的搜索页。
这种模式有以下特点:
- 页面上有一连串的页码,和电梯按钮相似
- 通过页码进行分页
- 通过点击上/下页按钮可实现页面切换
- 通过点击页码可实现页面切换
- 可直接跳转至指定页面
- 需要计算总数or总页数
一般来说前端传给后端的请求中,仅包含 page
和 per_page
,前者来标明要第几页,后者标明每页的大小,例如 page=2&per_page=20
意味着请求第二页,页中的数据有20条。这种模式多用于 PC 端,适合需要查找特定内容的页面。
其实在数据库中对应的语句如下(Mysql):
1 | select * from TABLE_NAME where ... order by ... limit ({page} -1) * {per_page}, {per_page} |
缺点
** 数据缺失 **
获取后页时,前页数据有删除。此时,本应出现在后页的内容被“顶”到前页,而前页已经加载过了不会重新加载,后页又无此内容,从而无法被用户看到。
举个栗子说明下:
- 当前数据库中的内容已经按照
7-6-5-4-3-2-1
的顺序 - 第一次请求
page=1&per_count=2
则返回7-6
- 之后数据库内容
6
被删,现在的序列为7-5-4-3-2-1
- 第二次请求
page=2&per_count=2
则返回4-3
,本来应该返回5-4
,这样5
就从请求中消失了。
** 数据重复 **
获取后页时,前页数据有插入。此时,原本在前页的内容被“压”到后页,导致前后也都有此数据,在用户端就是重复数据。
这里也举个栗子说明:
- 当前数据库中的内容已经按照
7-6-5-4-3-2-1
的顺序 - 第一次请求
page=1&per_count=2
则返回7-6
- 之后数据库插入内容
8
,现在的序列为8-7-6-5-4-3-2-1
- 第二次请求
page=2&per_count=2
则返回6-5
,本来应该返回5-4
,这样6
就从请求中重复了。
** 性能问题 **
较大页码的数据获取时性能会下降,计算总数也会带来额外的开销。
适用场景
- 用户在查看列表的时候,列表中的数据不会发生增删的情况。
- 数据量不是很大的情况下
扶梯式
扶梯式分页模式更多在移动端实现,存在以下特点:
- 通过滚动/上拉/点击等方式加载新一页
- 无页码
- 无上/下页按钮
- 不可跳转至指定页面
- 无需计算总数or总页数
该种方法传入的参数有 since_id
、max_i
和count
。
客户端第一次请求的时候,仅需携带 count
参数,对应的数据库语句如下(Mysql):
1 | select * from TABLE_NAME order by id DESC limit {count} |
这里的id
是递增的。
举个栗子说明下:
当前数据库中的内容id是 7-6-5-4-3-2-1
, count=2
时,那么返回的是 7-6
。
第二次访问的时候,就需要携带 since_id
或max_id
了。如果想要获取id值比6
小的内容,那么除了count=2
之外,还要携带 max_id=6
的参数。后端的处理语句类似:
1 | select * from TABLE_NAME where id < {max_id} order by id desc limit {count} |