element-ui中Select 选择器异步加载下一页
场景
当我们使用 Select 选择器存放大量数据的时候。
会发现存在这么2个问题。
1.接口响应时间较长。(因为数据量较多,一次查询的所有)甚至有可能超时。
2.前端下拉框滑动卡顿。
这个时候们如何解决上面面临的问题呢?
有的小伙伴可能会说:
1.分页加载。确实是可以解决问题。
2.页面卡顿使用虚拟dom.超时喊后端自己优化代码
因为项目中使用的是 element-ui,没有虚拟加载。
不想 Ant Design Vue一样,有virtual属性,设置 true可以开启虚拟滚动。
这里就感觉到 Ant Design Vue处理的比element-ui好一些(希望各位大佬不要喷我)
所以我们只能选择异步加载。
当页面滑动到底部的时候,加载下一页的数据
下拉框滑动到底部触发事件,加载下一页的数据
我们首先需要做到的是:获取下拉框元素的DOM节点。
由于一个页面可能有多个dom节点。
我们需要使用 popper-class属性来设置 Select 下拉框的类名。
然后后通过dom.scrollHeight - dom.scrollTop <= dom.clientHeight
来判断是否触底了。下面我们就来实现了一下
// 子组件
<template>
<div>
<el-select popper-class="more-next-box" v-model="value" placeholder="请选择">
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value">
</el-option>
</el-select>
</div>
</template>
<script>
export default {
data() {
return {
options: [{
value: '001',
label: '数据1'
}, {
value: '002',
label: '数据2'
}, {
value: '003',
label: '数据3'
}, {
value: '004',
label: '数据4'
}],
value: ''
}
},
mounted() {
// 获取dom节点
const domElementNode = document.querySelector('.more-next-box .el-select-dropdown__wrap')
// 注册下拉滚动事件
domElementNode.addEventListener('scroll', ()=>{
const isBottom = domElementNode.scrollHeight - domElementNode.scrollTop <= domElementNode.clientHeight
if (isBottom) {
console.log('是否到底了')
}
})
},
}
</script>
现在我们已经成功判断是否下拉到底了。
接下来我们应该去封装一下这个组件的属性和事件
争取拥有官方文档中的所有属性和事件
如何让 el-select拥有官方的所有属性呢?
我们可以使用props属性,一个一个的写上去。
这个操作虽然可以但是官方文档多20+多个属性。
是不是太麻烦了呀?
我们可以使用 $attrs配合inheritAttrs: false 来解决这个问题
$attrs里面包含着父组件传递的所有数据(除style和class)。
当一个组件声明了prop时候,attrs里面包含除去prop里面的数据剩下的数据。
而inheritAttrs: false,表示将属性不设置在根元素上,而是设置在指定的元素上
//组件优化为
<template>
<div>
<el-select v-bind="$attrs" v-model="value" >
<el-option v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</div>
</template>
//表示不添加在组件的根元素上
inheritAttrs: false,
//页面使用
<SelectLoadMore
popper-class="more-next-box"
:clearable="true"
placeholder="请选择数据">
</SelectLoadMore>
如何让 el-select拥有官方的所有事件呢?
$listeners包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。
//组件再次优化为
<el-select v-on="$listeners" v-bind="$attrs" v-model="value" >
<el-option v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
//页面使用
<div>
<SelectLoadMore popper-class="more-next-box"
:clearable="true"
@visible-change="visibleChange"
placeholder="请选择数据"
@change="changeHandler"
></SelectLoadMore>
</div>
el-option中key,label,value这些字段不一样怎么处理?
这个我们直接使用props来处理就好了。
这样我们就需要担心key键名不一致这个问题了
<div>
<el-select v-on="$listeners" v-bind="$attrs" v-model="value" >
<el-option v-for="item in options"
:key="item[setKey]"
:label="item[setLabel]"
:value="item[setValue]">
</el-option>
</el-select>
</div>
props: {
setKey: {
type: String,
default:'key'
},
setLabel: {
type: String,
default: 'label'
},
setValue: {
type: String,
default: 'value'
}
},
下拉到底部加载下一页的数据
// 组件
created() {
this.getList()
},
mounted() {
// 获取dom节点
const domElementNode = document.querySelector('.more-next-box .el-select-dropdown__wrap')
// 注册下拉滚动事件
domElementNode.addEventListener('scroll', ()=>{
const isBottom = domElementNode.scrollHeight - domElementNode.scrollTop <= domElementNode.clientHeight
if (isBottom) {
console.log('是否到底了')
// 这里应该还有一个判断,总条数和当前列表中的数据做一个比较。
// 这里我就不写了
this.getList(this.pageSize += 1)
}
})
},
methods: {
// 请求的数据
getList(index = 1) {
let arr = []
setTimeout(() => {
for (let i = 0; i < 10; i++){
arr.push({
value: (index-1) *10 + i,
label: '数据'+ (index - 1) * 10 + i
})
}
this.options=this.options.concat(arr)
},2500)
}
},
加载下一页的时候做一个加载提示
从上面这张图中,我们可以看见。
加载的时候没有任何的提示和效果。非常的不友好。
用户都不知道是在加载数据。我们现在优化一下
//子组件
<el-select v-on="$listeners" v-bind="$attrs" v-model="value" >
<el-option v-for="item in options"
:key="item[setKey]"
:label="item[setLabel]"
:value="item[setValue]">
</el-option>
<el-option v-show="loadingFlag" label="正在努力加载中" value="xx01"></el-option>
</el-select>
data() {
return {
options: [],
value: '',
pageSize: 1,
loadingFlag:false //控制加载显示的
}
},
getList(index = 1) {
this.loadingFlag=true //开启加载中
let arr = []
setTimeout(() => {
for (let i = 0; i < 10; i++){
arr.push({
value: (index-1) *10 + i+'',
label: '数据'+ (index - 1) * 10 + i
})
}
this.options = this.options.concat(arr)
this.loadingFlag = false //加载结束
},2500)
}
md发现了一个bug,用户选择‘正在努力加载中’中这一项怎么办?
有细心的小伙伴,可能会说:
<el-option v-show="loadingFlag" label="正在努力加载中" value="xx01"></el-option>
这样写真的没有问题吗?
万一用户选择了,这一项数据怎么办?
这,这,这,这,这恐怕就会100%出现bug了。
使用 disabled 来处理禁止选择这一项。
哈哈··,我简直是一个小天才(手动狗头)
使用 disabled 来处理禁止选择这一项
<el-select v-on="$listeners" v-bind="$attrs" v-model="value" >
<el-option v-for="item in options"
:key="item[setKey]"
:label="item[setLabel]"
:value="item[setValue]">
</el-option>
<el-option disabled v-show="loadingFlag" label="正在努力加载中" value="xx01"></el-option>
</el-select>
继续优化加载动画
有的小伙伴说,这个虽然不能选择,也有加载提示。
但是是灰色的,不太友好。
你他娘的,就你屁事多。烦死了(悟空表情)。
<el-select v-on="$listeners" v-bind="$attrs" v-model="value" >
<el-option v-for="item in options"
:key="item[setKey]"
:label="item[setLabel]"
:value="item[setValue]">
</el-option>
<p v-show="loadingFlag">正在努力加载中</p>
</el-select>
我们可以自己用一个标签来处理动画,这里我就不在做动画了。
其实如果你觉得 disabled的样式也可以修改的,
其实中的disabled也是可以修改样式的
<el-option disabled v-show="loadingFlag" label="正在努力加载中" value="xx01"></el-option>
<style lang="scss" scoped>
.el-select-dropdown__item.is-disabled{
color: red;
}
</style>
如何做数据回填
有些时候,我们除了需要选择值,还需要回填值。
我们可以使用 props来传递值,然后在created中赋值。
// 数据回填的值
writeData: {
type: String,
default: ''
}
created() {
if (this.writeData) {
this.value = this.writeData
}
this.getList()
},
全部代码
// 使用的页面
<template>
<div>
<SelectLoadMore popper-class="more-next-box"
:clearable="true"
:writeData="writeData"
@visible-change="visibleChange"
placeholder="请选择数据"
@change="changeHandler"
></SelectLoadMore>
</div>
</template>
<script>
import SelectLoadMore from "@/components/SelectLoadMore.vue"
export default {
components: {
SelectLoadMore
},
data() {
return {
writeData: 108
}
},
methods: {
changeHandler() {
console.log('值发生了改变')
},
visibleChange(flag){
console.log('flag', flag)
}
},
}
</script>
组件
<template>
<div>
{{ value }}
<el-select v-on="$listeners" v-bind="$attrs" v-model="value" >
<el-option v-for="item in options"
:key="item[setKey]"
:label="item[setLabel]"
:value="item[setValue]">
</el-option>
<!-- <p v-show="loadingFlag">正在努力加载中</p> -->
<el-option disabled v-show="loadingFlag" label="正在努力加载中" value="xx01"></el-option>
</el-select>
</div>
</template>
<script>
export default {
//表示不添加在组件的根元素上
inheritAttrs: false,
props: {
setKey: {
type: String,
default:'key'
},
setLabel: {
type: String,
default: 'label'
},
setValue: {
type: String,
default: 'value'
},
// 数据回填的值
writeData: {
type: String,
default: ''
}
},
data() {
return {
options: [],
value: '',
pageSize: 1,
loadingFlag:true
}
},
created() {
if (this.writeData) {
this.value = this.writeData
}
this.getList()
},
mounted() {
// 获取dom节点
const domElementNode = document.querySelector('.more-next-box .el-select-dropdown__wrap')
// 注册下拉滚动事件
domElementNode.addEventListener('scroll', ()=>{
const isBottom = domElementNode.scrollHeight - domElementNode.scrollTop <= domElementNode.clientHeight
if (isBottom) {
console.log('是否到底了')
// 这里应该还有一个判断,总条数和当前列表中的数据做一个比较。
// 这里我就不写了
this.getList(this.pageSize += 1)
}
})
},
methods: {
getList(index = 1) {
this.loadingFlag=true
let arr = []
console.log('index', index)
setTimeout(() => {
for (let i = 0; i < 10; i++){
arr.push({
value: (index-1) *10 + i+'',
label: '数据'+ (index - 1) * 10 + i
})
}
this.options = this.options.concat(arr)
// this.loadingFlag = false
},1500)
}
},
}
</script>
<style lang="scss" scoped>
.el-select-dropdown__item.is-disabled{
color: red;
}
</style>
尾声
如果你觉得,这篇文章写的不错,对你有用。
请给我点一个赞,感谢了。
遇见问题,这是你成长的机会,如果你能够解决,这就是收获。
作者:晚来南风晚相识
出处:https://www.cnblogs.com/IwishIcould/
本文版权归作者所有,欢迎转载,未经作者同意须保留此段声明,在文章页面明显位置给出原文连接
如果文中有什么错误,欢迎指出。以免更多的人被误导。
出处:https://www.cnblogs.com/IwishIcould/
想问问题,打赏了卑微的博主,求求你备注一下的扣扣或者微信;这样我好联系你;(っ•̀ω•́)っ✎⁾⁾!
如果觉得这篇文章对你有小小的帮助的话,记得在右下角点个“推荐”哦,或者关注博主,在此感谢!
万水千山总是情,打赏5毛买辣条行不行,所以如果你心情还比较高兴,也是可以扫码打赏博主(っ•̀ω•́)っ✎⁾⁾!
想问问题,打赏了卑微的博主,求求你备注一下的扣扣或者微信;这样我好联系你;(っ•̀ω•́)っ✎⁾⁾!

支付宝

微信
如果文中有什么错误,欢迎指出。以免更多的人被误导。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
2021-06-08 熬夜讲解vue3组合API中setup、 ref、reactive的用法以及hooks的使用
2019-06-08 vue中如何使用echarts,使用axios获取数据