第6节:远程搜索弹窗(remote 模式),弹窗滚动加载分页
本节重点:
remote远程搜索,输入关键词请求接口,还有下拉弹窗滚动监听实现分页加载选项。区分【本地过滤 filterable】和【远程搜索 remote】。
核心知识点
remote:开启远程搜索,不再前端本地过滤 ,需要自己在remote-method里面请求后端remote-method:回调函数,参数是用户输入的搜索关键词,输入内容变化就触发loading:el-select 的 loading 属性,请求接口时显示加载动画popper实例获取:拿到弹窗 DOM,监听 scroll 事件,实现滚动到底加载更多
⚠️ 坑点:remote 模式下,必须手动清空选项列表,关键词变化要重置页码
完整可测试代码
<template>
<div class="container">
<h4>远程搜索下拉,弹窗滚动分页加载</h4>
<el-select
v-model="selectVal"
filterable
remote
:remote-method="handleRemoteSearch"
:loading="loading"
placeholder="输入关键词远程搜索"
style="width:320px"
@visible-change="handleVisibleChange"
>
<el-option
v-for="item in optionList"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
<p>选中值:{{ selectVal }}</p>
</div>
</template>
<script setup>
import { ref } from 'vue'
const selectVal = ref('')
const loading = ref(false)
const optionList = ref([])
const page = ref(1)
const pageSize = 5
const keyword = ref('')
// popper DOM
let popperDom = null
// 模拟接口请求
const fetchData = async (searchKey, p) => {
// 模拟网络延迟
await new Promise(resolve => setTimeout(resolve, 600))
// 模拟后端返回数据
const mockArr = []
for(let i = 1; i <= pageSize; i++){
const id = (p -1)*pageSize + i
mockArr.push({
id,
name: `${searchKey || '全部'} 选项-${id}`
})
}
return mockArr
}
// 远程搜索触发
const handleRemoteSearch = async (val) => {
keyword.value = val
page.value = 1 // 关键词改变,重置页码
loading.value = true
const res = await fetchData(val, page.value)
optionList.value = res
loading.value = false
}
// 弹窗打开/关闭
const handleVisibleChange = async (isOpen) => {
if(isOpen){
// 弹窗打开,等待popper渲染完成,获取弹窗滚动容器
setTimeout(()=>{
popperDom = document.querySelector('.el-select__popper .el-scrollbar__wrap')
if(!popperDom) return
// 绑定滚动事件
popperDom.addEventListener('scroll', handlePopperScroll)
}, 100)
}else{
// 弹窗关闭,移除滚动监听,防止多次绑定
if(popperDom){
popperDom.removeEventListener('scroll', handlePopperScroll)
popperDom = null
}
}
}
// 弹窗滚动事件
const handlePopperScroll = async () => {
const { scrollTop, scrollHeight, clientHeight } = popperDom
// 判断滚动到底部,预留20px容错
if(scrollTop + clientHeight >= scrollHeight - 20 && !loading.value){
loading.value = true
page.value +=1
const res = await fetchData(keyword.value, page.value)
// 追加数据
optionList.value.push(...res)
loading.value = false
}
}
</script>
<style scoped>
.container {
padding:20px;
}
</style>
测试步骤
- 输入文字,触发远程搜索,看到 loading 加载动画,列表刷新
- 下拉弹窗滚动到底部,自动加载下一页数据,追加到弹窗列表
- 修改搜索关键词:页码重置,列表重新请求第一页
- 关闭弹窗:滚动事件解绑,避免多次监听内存泄漏
业务坑总结
- remote 开启后,原生前端过滤失效,所有过滤逻辑交给后端
- visible-change 打开弹窗拿 popper DOM,一定要加 setTimeout,等弹窗 DOM 渲染完成
- 弹窗关闭必须 removeEventListener,否则多次打开弹窗会重复绑定滚动事件
- 滚动加载要加 loading 锁,防止短时间多次触发请求
