近些年写了太多的列表页面, 也见过很多人写的列表组件代码, 这里分析并记录一下个人认为的最佳实践方案
按照常规的需求先说列表需要的数据(加载数据过程中的loading等状态不在本文的考虑范围内):
- 1. 列表页面首要保存的肯定是要展示的数据
list:Array
了 - 2. 一般来说有列表大部分会有翻页功能, 所以我们需要
pageNo:Number, pageSize:Number
- 3. 有了翻页功能, 大家一般都会展示一下总的数量, 再加一个
total:Number
- 4. 某些列表可能需要一些筛选条件, 也就是搜索功能, 这些筛选条件各不相同, 所以我们再加一个
filter:
我见过很多开发者自从学习了"页面无刷新"这个概念之后, 翻页功能直接就是请求数据, 然后刷新列表, 从不考虑用户的实际使用情况, 尤其是后台项目开发中, 总是能碰到 类似于 [ 用户在翻页或者修改每页显示条数或者修改筛选条件等操作后, 刷新浏览器页码自动回到第一页, 或者回到了初始化的"每页10条"数据 ] , 或者丢失了筛选参数
所以这是我们做列表页首先要面对的两个问题:
- 1. 在翻页过程中, 用户通过浏览器工具栏回退页面时, 是否能正常回到上一页
- 2. 用户刷新浏览器后, 能不能继续浏览当前页码的数据
如果你的列表中允许用户添加筛选条件, 同样要注意另外两个问题:
- 1. 页面回退后, 已选的筛选条件能否回到上一个页面的状态
- 2. 页面刷新后, 已选的筛选条件能否保留
基于这四点, 我的常用做法是, 把页码相关的数据都放在url上, 以vue3为例
<template><pagination:total="page.filter.total":pageNo="page.filter.pageNo":pageSize="page.filter.pageSize"@changePageNo="(pageNo:number)=>go({pageNo})"@changePageSize="(pageSize:number)=>go({pageSize})"/></template><scriptlang="ts"setup>constroute=useRoute(); constrouter=useRouter(); constpage=rective<any>({ list:[], total:0, filter:{ pageNo: 1, pageSize: 10, name:'' } }) watch(route,()=>{ // 监听路由变化getList() }); onMounted(()=>{ getList() }) asyncfunctiongetList(){ try{ /* 这里是为了合并路由上的参数与filter的已有参数 放在这个位置, 是因为 onMounted 和 RouteChange 时都要执行这个函数, 放这里就不用写重复代码了 */page.filter=Object.keys(route.query).reduce((current,next)=>{ // 只覆盖不为undefined或者null的属性值if(route.query[next] !==undefined&&route.query[next] !==null){ current[next] =route.query[key] } returncurrent }, page.filter) constres=http.get(url,{ params:page.filter } // 这里假设你的数据是没问题的page.list=res.data.list; page.tota=res.data.total; }catch(error:any){ console.log(error) } } functiongo(condation:{}){ router.push({ name: route.name, query:{ // .. 把传递进来的新参数与旧参数合并传递给下一页route.query, condation } }) )
当然了, 如果你的项目确实不需要保留page和filter数据, 就没上面这么麻烦了