这是我参与更文挑战的第2天,活动详情查看: 更文挑战
几天前,刚毕业新入职的小鲜肉拿着入职练习题自信满满地交差。其中有一个需要自己的一个数据列表后端接口,和对应的分页列表UI。看小鲜肉的介绍演示,这个列表接口就是个简单的没有任何参数的GET
请求。
我问他:“这个列表怎么做的分页?”
他回答:“额,数据传进(第三方)组件就分页了。”
小哥一派天真无邪,看得老神棍我直叹气,这是上门被坑的节奏。
老神棍多年的经验表示:列表必须在数据取出的时候就分页!!!除非百分百确定是几千条以内不会大幅长的列表(比如城市)。
后端的小伙伴尤其是同样经验欠缺的萌新,这个时候会换着花样坑我们天真善良的小前端:
“放心吧!这列表不会怎么长的!”然后三个月内暴涨到上万条。
“分页什么的,不行,做不到!”小前端不太明白为撒做不到,想想人家才是专业的,默默闭上了嘴。
撕逼没有足够准备,当然撕不过!今天我们来讲讲两种常见的分页API,为你下次撕逼加满油!
NO.1 offset, limit, total
- offset ——第一条返回数据的index
- limit ——返回多少条数据
- total——总的数据量
如果是GET
请求,URL查询字符串中需要带参数offset
(为0
时可以不带)及limit
:
GET /api/list?offset=0&limit=3
复制代码
在成功返回的数据中可以看到额外的total
值,如下:
{
"data": [
{ "id": 1 },
{ "id": 2 },
{ "id": 3 }
],
"total": 1234
}
复制代码
根据以上3个数据,可以显示出电脑端常见的分页UI(下图来自Ant Design Pagination组件:
显示以上UI的时候,组件需要计算出:
- 当前页:
Math.ceil(offset / limit + 1)
或(offset / limit + 1) << 1
。一般每页数据量相同的可以忽略向上取整的计算。 - 总页数:
Math.ceil(total / limit)
或(total / limit) << 1
。 - 以及点击页数发请求的时候根据页号反向计算出
offset = (page - 1) * limit
这个API设计比较基础,可同时用于以上电脑端的显示以及手机端常见的懒加载(无限下滑)显示。但是记得它是有缺点的:
- 它无法保证下一页拉出来的东西在上一页没有显示过。 用户在看完第一页切换到第二页的时候会有一段时间差,而这段时间差内很可能数据有更新。这种情况下如果数据采取了最新的数据在第一条显示的排序策略,那极有可能在第二页会重复看到第一页最下方几条看过的数据。而且前端是不便于做去重的,会导致页面显示的层次不齐。
- 当数据量大到一定程度,即时计算总数对数据端其实是种很大的负担。数据库需要把符合条件的所有数据拉出来一个个数这样的:woman_facepalming:,除非专门做个额外的参数表计数。而现在处理超大数据通常会分表甚至分数据库,这为找到指定页面的数据和统计总数增加了技术复杂度。所以很多时候后端小伙伴坑蒙拐骗也是无奈之举(但不代表我们不能为了性能和需求争取)。
NO.2 token, limit, next token
- token——返回结果第一条数据的“令牌”
- limit——返回多少条数据
- next_token——下一页的第一条数据“令牌”
- previous_token——上一页的第一条数据“令牌”(可选)
这个API在Google的列表设计模式中有相关说明,可以解决上面那种API的缺点。
如果是GET
请求,URL查询字符串中需要带参数token
(首页可不带)及limit
:
GET /api/list?token=abc&limit=3
复制代码
在成功返回的数据中可以看到额外的token,如下:
{
"data": [
{ "id": 1 },
{ "id": 2 },
{ "id": 3 }
],
"next_token": "dfe"
}
复制代码
那不同于第一种API,你很快也许发现了上面介绍的那种分页UI是无法实现的。因为我们无法知道总页数,当前第几页。换句话说,你的UI只可以显示“下一页”和“上一页”的按钮或者做无限滚动懒加载,不可以跳页面。而且,只有在返回数据中有next_token
的时候显示“下一页”的选项,在有previous_token
的时候显示“上一页的选项。
这意味着UI设计受限,你必须提前让产品和设计师明白自由跳页的设计没法做,因为大部分人习惯了那种分页方式,甚至以为这个是默认选项,别自顾自做完到时候受责怪。
乍看之下这种有限制的设计似乎并不合理,它限制了我们查看数据的自由。但它其实是很合理的,因为不论是管理系统、搜索引擎、电商平台产品列表,用户是很少会有需求跳着翻页的。而这个简化可以极大降低对数据端的负担,何乐而不为?
小结
嗯,下次撕逼不要丢蛋黄酱的脸。