
有效缓解大数据!使用Ant Design a-select实现滚动分页并回显指定数据
1.前言
在日常业务开发中,下拉框是一个非常常用的组件,使用起来也非常的简单,只需要引入组件并配置数据源即可完成基础功能的使用。
<a-select
:disabled='disabled'
v-model='model.idList'
placeholder='请选择'
mode='multiple'
>
<a-select-option v-for='item in dataList' :key='item.id' :value='item.id' :label='item.name'>
{{ item.name }}
</a-select-option>
</a-select>
2.问题浮现
本来一切正常的使用着a-select
,但就在今天,有个下拉框返回的数据量过大,导致下拉框加载非常的卡顿,于是需要将该下拉框改为了分页查询,每次查询20条数据,滚动到底部时再查询20条,以此类推实现滚动分页,这个功能并不难实现。只需要在组件的滚动监听上绑定触底查询方法即可。
<a-select
@popupScroll="scrollEvent" --滚动监听,滚动到底部时查询下一页数据
@search='searchEvent' --输入监听,用于条件查询
@blur='blurEvent' --失焦监听,输入搜索值后直接失焦需要清空查询条件,否则查询数据会残留
:disabled='disabled'
v-model='model.idList'
placeholder='请选择'
mode='multiple'
>
<a-select-option v-for='item in dataList' :key='item.id' :value='item.id' :label='item.name'>
{{ item.name }}
</a-select-option>
</a-select>
/**
* @author BoJack
* @description 项目下拉框滚动事件
* @date 2025/3/27 20:00
* @param event 滚动事件
**/
scrollEvent(event) {
const { target } = event
const scrollHeight = target.scrollHeight - target.scrollTop
const clientHeight = target.clientHeight
console.log('滚动条', scrollHeight, clientHeight)
// 判断下拉框滚动条到达底部
if (scrollHeight == clientHeight) {
//调用查询接口,scrollParam存放分页数据,此处无需再传入搜索值,若搜索框有值的话,滚动查询时会自动带上该搜索值
this.pageSelectList(this.scrollParam)
}
},
/**
* @description 分页查询列表下拉框
* @param scrollParam 分页参数
* @param searchValue 搜索值
**/
pageSelectList(scrollParam, searchValue) {
//搜索值
scrollParam.searchValue = searchValue
getAction(this.url.pageList, scrollParam).then((res) => {
if (res.success) {
this.dataList = this.dataList.concat(res.result)
//查询成功后页码+1
this.scrollParam.scrollPage++
} else {
this.$message.warning(res.message)
}
})
},
/**
* @author BoJack
* @description 输入文字搜索
* @date 2025/3/28 10:07
* @param searchValue 搜索值
**/
searchEvent(searchValue) {
// 查询前清空列表
this.propertyList = []
// 设置查询页码为1
this.scrollParam.scrollPage = 1
this.pageSelectList(this.scrollParam, searchValue)
},
随后回到服务端写完查询接口,保存编译运行一气呵成。到这一步,一切都是那么的顺利。回到页面上一看,发现滚动分页功能一切正常,但大数量时的回显是个问题,如果保存了一条排名60的数据,那么再次打开页面时,默认分页只查询了前20条数据,但该数据在60的位置,所以该数据依然不能正常显示(无法获取到对应name
,会直接显示id
在下拉框里)。
方案一
于是我想,既然需要回显该数据,那干脆回显时多查一次,查询时先单独用WHERE
条件查询到该数据,将其存到list中,随后再进行分页查询,然后将两次查询的list合并后统一返回给前端,这样,下拉框里就一定会有回显的那条数据了,但是,想到这里,又有一个问题,如果按上述逻辑,先行查询回显数据,再分页查询剩余数据,那我向下滚动三次,当分页数据到达60的时候,岂不是该数据就会重复显示两次,那这样的话就只能对List进行去重,想到去重,我首先想到了Set
,但关键是该List中存放的是引用数据类型(也就是Object
),而非基础数据类型,所以并不能通过Set进行直接去重,恐怕只能通过循环遍历进行去重,本来查询两次数据库代码效率就已经比较低下了,再循环遍历,效率又要大打折扣,所以果断放弃了该方案。
方案二
既然查询两次数据库造成性能浪费,那不如考虑能否一次查询到回显数据和分页数据?似乎并不难实现,只需要把回显的id传入SQL里,使用OR
条件同时查询到分页数据和id IN ('回显数据id')
即可,这一下,性能提升不少,毕竟减少了一次数据库的查询。
SELECT id
name,
code
FROM pro_info pi
WHERE (pi.id IN
('aaaa', 'bbbb')) --此处替换为mybatis循环idList数据
OR (pi.name = '' AND pi.code = '') --此处为其余的查询条件
LIMIT 20; --分页20条数据
到这一步,似乎是个完美方案,但是,我又发现,虽然该查询SQL能够正常查询到数据,但因为回显数据和分页数据是同时返回的,如果回显数据排名在20以后,那LIMIT 20
分页时将直接丢弃该回显数据,这很明显是不可以的,我们可以丢弃分页中的数据(反正滚动到下一页他就又出来了)但回显数据是必须要显示的,所以应该想办法把回显的数据排序到最前排,保证分页时不会被丢弃。
方案三(完美方案)
想到排序,那肯定是通过ORDER BY
了,但通常情况下使用order by都是对某个字段进行排序,那该如何将指定id的数据显示到前排呢?毫无头绪。
功力不够,ai来凑,把这个需求丢给ai后,ai给了我完全没使用过的一种全新写法,完美解决了该问题。
SELECT id
name,
code
FROM pro_info pi
WHERE (pi.id IN
('aaaa', 'bbbb'))
OR (pi.name = '' AND pi.code = '')
ORDER BY CASE
WHEN pi.base_id IN ('aaaa', 'bbbb') THEN 0
ELSE 1
END,
pi.code
LIMIT 20;
通过CASE WHEN
表达式在这里的作用是为每条记录生成一个排序值。具体逻辑为:
如果 pi.id
在指定的列表中(即 'aaaa'
,'bbbb'
)则返回 0
。否则,返回 1
。ORDER BY
子句首先根据 CASE WHEN
表达式的结果进行排序。由于 0 小于 1,因此返回 0 的记录(即 id 在指定List中的记录)会被优先排序到结果集的前面。
从而实现指定id显示在前面,其余数据按code
进行次要排序。
总结
出现后端大数据返回给前端时,可以通过分页的方式来解决大数据量的卡顿,但在对下拉框组件、树形组件或其他滚动型的组件进行分页时,应注意已有数据回显,查询时应当优先显示该数据,否则会被LIMIT
截断导致回显数据无法正常显示。而通过 CASE WHEN
表达式对指定id进行排序可以很好的解决这一问题。
感谢你看到这里,希望这篇文章能够帮到你,如果你有更好的解决方案,欢迎到评论区留言!
Make Java Great Again!