前情提要
最近做了一个小优化,还是关于展示大屏方面的。大屏中使用el-table展示列表数据,最初的方案是将数据全部返回,确实随着数据变多有性能问题,有时请求时间比较长。这里做的优化就是实现列表的滚动到距离底部一定高度时再次请求接口数据,实现分批请求提升性能。然后这里也实现让el-table自动滚动的功能
思路
(1) 自动滚动:
先给el-table绑定ref,拿到组件实例,找到真实滚动的那部分内容。这一步需要耐心点。然后就是监听这个滚动区域的滚动事件,利用视图展示区域(clientHeight)+垂直方向超出部分(scrollTop)和 内容实际的总高度三者的(scrollHeight)的关系判断内容是否滚动到底部,需要添加定时器增加scrollTop的高度,以达到自动滚动的效果,最终滚动完毕给出判断回到el-table顶部重新滚动(scrollTop置为0)。
备注:最初是查找官方文档,发现使用el-tbale的话没有直接可以支持自动滚动的属性,但是vue3中有v-infinite-scroll这个指令,不过我没试过放在el-table中使用,有机会各位可以去尝试一下。
(2) 分批次请求数据
还是要先拿到实际滚动的那块区域,然后还是监听滚动事件,由于增加了自动滚动,我开始的思路是把自动滚动和用户手动滚动两个情况分开,不过我在review的时候尝试了一下,确实是不需要区分的,不管是自动滚动还是手动滚动这里只需要考虑好滚动到合适的高度请求数据即可,拼接在之前的tbaleData中,直至达到总数停止
自动滚动
代码:
//自动滚动封装在table中,是否自动滚动由父组件传入autoScroll决定
//onMounted:组件挂载完成后执行。若autoScroll为真,则调用 //createScroll初始化滚动功能。
onMounted(() => {
props.autoScroll && createScroll()
})
//onUnmounted:组件卸载前执行。若autoScroll为真,则调用 //clearScroll清理滚动相关资源
onUnmounted(() => {
props.autoScroll && clearScroll()
})
const clearScroll = () => {
if(!props.autoScroll) return
clearInterval(timer)
timer = null
}
const createScroll = () => {
if(!props.autoScroll) return
clearScroll()
// 拿到 table
const table = tableref.value.layout.table.refs
// 拿到可以滚动的元素
const tableWrapper = table.bodyWrapper.firstElementChild.firstElementChild
timer = setInterval(() => {
tableWrapper.scrollTop += 1.5
// 判断是否滚动到底部,如果到底部了置为0(可视高度+距离顶部=整个高度)
if (tableWrapper.clientHeight + tableWrapper.scrollTop >= (tableWrapper.scrollHeight - 5))
tableWrapper.scrollTop = 0
}, 100)
}
分批请求数据
代码:
const jyTableRef = ref<InstanceType<typeof ElTable>>()
const wrapRef = ref() // 滚动条实例
const pageNum = ref(1)
const count = ref('0') // 设备总数
const isRequest = ref(true) // 控制请求的时机
// 获取实时监测表格数据
async function getRealTimeModelData(id: any) {
if (mockData.value.length > +count.value) return // 如果已经超出最大范围,停止查询
const result = await getRealTimeModelDataApi({
groupId: id,
pageSize: 50,
pageNum: pageNum.value,
// 环境变量
// tenantId: 'jybfgs'
})
console.log('result', result)
count.value = result.count || 0 // 从图表的接口获取总条数,列表的总数会变不能用
mockData.value.push(...(result.list || [])) // 将每次请求的数据拼接缓存在前端
// isUserScroll.value = 2
isRequest.value = false
// mockData.value = res.list || []
sortTableData(sortName.value, sortType.value)
filterHandler(filterDeviceStatus.value, filterProp.value)
}
// 滚动事件
const scrolling = async (event:any) => {
console.log('scrolling')
const scrollHeight = event.target.scrollHeight
const clientHeight = event.target.clientHeight
// isScrollingBottom.value = scrollHeight - clientHeight === event.target.scrollTop// 是否滚动的底部
console.log('event', isRequest.value, scrollHeight, clientHeight, (scrollHeight - clientHeight), event.target.scrollTop)
// 有时接口数据返回较慢,这里不在触底时请求刷新,而是在内容达到一半时请求新数据
if (((scrollHeight - clientHeight) / 2) <= event.target.scrollTop && !isRequest.value) {
console.log('自动滚动')
isRequest.value = !isRequest.value
// 页码+1
pageNum.value++
getRealTimeModelData(props.id)
}
}
onMounted(() => {
getRealTimeModelData(props.id)
console.log(1234455, jyTableRef.value!.tableref.scrollBarRef.wrapRef)
// 获取表格内的滚动条,并监听滚动事件
wrapRef.value = jyTableRef.value!.tableref.scrollBarRef.wrapRef
// 监听滚动事件
wrapRef.value.addEventListener('scroll', scrolling)
})
onUnmounted(() => {
// if (timer) clearInterval(timer)
// 移出监听事件
wrapRef.value.removeEventListener('scroll', scrolling)
})
小结&收获
由于时间有限,这里的代码确实不能直接cv,我是直接从项目中截取的关键部分,省略了一些引用或者变量声明。对我自己来讲我已经测试很多遍了,所以才比较清晰。这里的代码主要是一个备份和引导作用。对大家来说我觉得重要的还是思路吧,我看大家常规思路都是这样做的,大家可以下去自己尝试。通过回顾之前开发的这个功能,我想再总结一下对于这个需求几个比较重要关键的技术点和经验吧:
(1)首先如果是使用开源的ui组件库比如elementui,那么肯定是先去官网看对应组件是否存在相关功能的属性和方法,如果没有可以直接使用的或者相关辅助我们实现功能的话再去自己百度也好或者AI调研开发。
(2)这次大屏的两个功能点都离不开的一点就是我们对于el-table都去给它绑定ref,目的就是拿到它的组件实例,然后可以方便我们操作上面的方法,dom和属性,以后的开发中类似的功能都是离不开组件实例的,经常会用到。这一点我认为需要多多练习,自然能熟能生巧。
(3)监听滚动事件。这一步算是这次迭代的核心内容吧。这里我们又要知道的两个东西,一个是监听滚动事件wrapRef.value.addEventListener('scroll', scrolling),addEventListener这个监听器非常好用,对于所有dom的相关操作都可以用它来监听。也是熟能生巧,其中的event形参中存在所有你想要的属性;另一个就是这次功能的判断条件(请求数据的时机)一个常见公式:
//当数据滚动到列表底部时(数据加载完毕时)此等式成立
内容总高度 - 视图高度 = 内容超出顶部的高度
scrollHeight - clientHeight = scrollTop
正常来讲(scrollHeight - clientHeight )的高度一定是大于 scrollTop的,只有当数据滚动到列表底部时此等式成立,也即是数据加载完的时候成立。所以常规思路就是利用这个时机去来触发新请求拼接新数据。当然这三个属性在监听的scrolling方法中的event形参中全都有。
不过在我的代码中我没有在触底的时候二次请求,而是选择在内容滚动到一半的时候就提前请求第二次的数据。
(((scrollHeight - clientHeight) / 2) <= event.target.scrollTop && !isRequest.value)
这里还准备了一个变量 isRequest去防止满足条件多次请求的情况,并且接口返回时间有时候比较长,由于自动滚动会导致触底回到列表第一条重头滚动,所以不选择在触底时请求而是在内容滚动一半就提前请求。
而且还有一点需要注意的是,条件一定不能给等于而是要给小于等于或者大于等于,因为当浏览器放缩不同时,是会导致这个高度不存在整数的情况,等式就不成立了,所以尽量给一个范围,这样就必然会被触发。
下面是菜鸟教程的原话:
这三者之间的关系不熟悉的话具体详解再推荐一篇文章给大家:
JavaScript第 11 篇,JavaScript中的scrollTop(JavaScript中的scrollTop,JS滚动到顶部)-CSDN博客文章浏览阅读1.9w次,点赞25次,收藏96次。⭐scrollTop是JavaScript中一个非常有用且重要的方法,它用于获取或设置元素的垂直滚动条位置,实现各种滚动相关的功能,无论是回到顶部、滚动到指定位置还是监听滚动事件,都需要用到scrollTop,在本文中,我们将深入了解scrollTop的用法和实际应用,这是一张scrollTop的关系图,仅供参考_scrolltophttps://blog.csdn.net/weixin_65793170/article/details/129836174(4)封装自定义指令。对于这种列表滚动来讲,一般需要操作dom的情况可以封装为自定义指令,然后原则就是“开箱即用”,尽量让别人直接使用即可,不需要添加太多逻辑,尽量封装到位。方便之后有需要滚动的table可以一次到位,方便之后开发。(前辈经验:如果是涉及到dom监听,操作相关的服用,自定义指令是非常好的方案)
分享
这里还有几篇我之前看到的思路差不多,这里给大家分享一下,便于大家加深理解(代码不重要,思路很重要)。
el-table不使用分页组件,仅滚动条实现加载下一页数据_el-table滚动加载-CSDN博客文章浏览阅读622次,点赞4次,收藏3次。el-table不使用分页组件,仅滚动条实现加载下一页数据_el-table滚动加载https://blog.csdn.net/csdnyp/article/details/136315835
使用el-table实现自动滚动_el-table自动滚动-CSDN博客文章浏览阅读1.4k次,点赞4次,收藏6次。在前端开发大屏的时候,我们会用到表格数据展示,有时候为了使用户体验更加好,会增加表格自动滚动。下边我将以示例代码,用element UI的el-table来讲一下。2 通过判断dom的scrollHeight和scrollTop的关系,来实现滚动。1 .增加dom监听,鼠标放上去的时候不滚动,鼠标离开的时候滚动。3.当不需要使用,或者表格需要重新渲染的时候,我们需要清掉定时器。_el-table自动滚动https://blog.csdn.net/weixin_38912662/article/details/140657947ant design vue中table表格滚动加载实现思路_vue.js_脚本之家在处理一写数据量特别大的情况下,我们不能把后端的数据一次性全部拿到前端在table表格中展示,为了考虑性能优化,使用了滚动加载表格数据,这篇文章主要介绍了ant design vue中table表格滚动加载实现思路,需要的朋友可以参考下https://www.jb51.net/javascript/323819ttr.htmel-table实现表格滚动上拉加载更多(Vue3)_el-table滚动加载-CSDN博客文章浏览阅读1k次。【代码】el-table实现表格滚动上拉加载更多(Vue3)_el-table滚动加载https://blog.csdn.net/qq_48665028/article/details/136883338