文章目录
移动端在渲染长列表时 大量dom节点的渲染和重绘重排会导致页面卡顿、滚动不流畅、设备耗电加快、影响移动设备电池寿命等性能问题
这里分享使用【虚拟滚动】方案进行长列表优化,以Vue3为例,推荐使用
vue-virtual-scroller
先列举
vue-virtual-scroller
相关官方文档帮助学习
- 官方文档
- Live demo
- Live demo 源码
- Video demo
安装 vue-virtual-scroller
npminstall--save vue-virtual-scroller@next
yarnadd vue-virtual-scroller@next
引入
安装所有组件:
import VueVirtualScroller from'vue-virtual-scroller'
app.use(VueVirtualScroller)
按需引入组件:
import{ RecycleScroller }from'vue-virtual-scroller'
app.component('RecycleScroller', RecycleScroller)
引入样式文件
import'vue-virtual-scroller/dist/vue-virtual-scroller.css'
📢注意事项
- 整个列表的高度要写死,不然会将整改列表作为可视区域,会出现渲染全部数据而不是只渲染可视区域的问题; 这里推荐使用
flex: 1;实现,比height: calc(100vh - 30vw);更优雅、更易维护 - 使用
-webkit-overflow-scrolling: touch;开启硬件加速,ios高版本自带 - 使用
overscroll-behavior: none;禁用iOS回弹效果
使用
使用
DynamicScroller
组件渲染不确定高度的组件
基础使用
<divclass="content-wrap"><DynamicScroller:items="dataList":min-item-size="160"key-field="id"class="virtual-scroller"><template#default="{ item, index, active }"><DynamicScrollerItem:item="item":active="active":size-dependencies="[item.status, item.type]":data-index="index"class="virtual-scroller-item"><!-- 渲染组件 --><TaskItem:data="item"/></DynamicScrollerItem></template></DynamicScroller></div>
.icc__container{height: 100vh;display: flex;flex-direction: column;box-sizing: border-box;.icc__content-wrap{flex: 1;.virtual-scroller{/* 开启硬件加速 -webkit-overflow-scrolling: touch; ios高版本自带 */-webkit-overflow-scrolling: touch;/* 禁用回弹效果 */overscroll-behavior: none;height: 100%;}}}
上拉加载
vant List
+
DynamicScroller
会导致连续触发
vant List
load 事件,所以只能手写上拉加载
- 监听
DynamicScroller滚动事件,如果当前距离顶部的值加上可视区域的值大于等于总高度,则滚动条触底,加载更多 - 使用
DynamicScroller``````after插槽,定义加载中、加载完成、加载失败等状态
<divclass="content-wrap"><DynamicScroller:items="dataList":min-item-size="160"key-field="id"class="virtual-scroller"@scroll="handleDynamicScrollerScroll"><template#default="{ item, index, active }"><DynamicScrollerItem:item="item":active="active":size-dependencies="[item.status, item.type]":data-index="index"class="virtual-scroller-item"><!-- 渲染组件 --><TaskItem:data="item"/></DynamicScrollerItem></template><template#after><divclass="after"><van-loadingv-show="loadMoreLoading">加载中...</van-loading><spanv-show="finished">没有更多了</span><spanv-show="loadError && !loadMoreLoading"@click="handleLoadMore">请求失败,点击重新加载</span></div></template></DynamicScroller></div>
// 上拉loadingconst loadMoreLoading = ref<boolean>(false)// 没有更多数据了const finished = ref<boolean>(false)// 加载失败const loadError = ref<boolean>(false)// 实现上拉加载consthandleDynamicScrollerScroll=(e: Event)=>{// 距顶部const scrollTop =(e.target as HTMLDivElement)?.scrollTop ||0// 可视区高度const clientHeight =(e.target as HTMLDivElement).clientHeight ||0// 滚动条总高度const scrollHeight =(e.target as HTMLDivElement)?.scrollHeight ||0// 触底距离const offset =300// 如果当前距离顶部的值加上可视区域的值大于等于总高度,则滚动条触底if(scrollTop + clientHeight >= scrollHeight - offset){if(!loadMoreLoading.value &&!finished.value &&!loadError.value){
console.log('滚动到底部了')
loadMoreLoading.value =truehandleLoadMore()}}}
下拉刷新
使用
vant PullRefresh
实现下拉刷新
如果直接用
vant PullRefresh
包裹虚拟滚动,会导致无法向下滑动,任何位置下拉都会触发下拉刷新。
解决方案:
1. `vant PullRefresh` 中有 `disabled` 选项,可以禁用下拉刷新,默认设置为 `false`
2. 监听滚动条滚动事件中的 `scrollTop`,
3. 如果 `scrollTop` 小于4,则将 `disabled` 变为 `false`
4. 否则将 `disabled` 变为 `true`
<template><divclass="container"><van-pull-refreshv-model="refreshLoading"@refresh="handlerefresh":disabled="disabledPullRefresh"class="content-wrap"><templatev-if="dataList.length > 0"><DynamicScroller:items="dataList":min-item-size="160"key-field="id"class="virtual-scroller"id="virtual-scroller"@scroll="handleDynamicScrollerScroll"><template#default="{ item, index, active }"><DynamicScrollerItem:item="item":active="active":size-dependencies="[item.status, item.type]":data-index="index"class="virtual-scroller-item"><!-- 渲染组件 --><TaskItem:data="item"/></DynamicScrollerItem></template><template#after><divclass="after"><van-loadingv-show="loadMoreLoading">加载中...</van-loading><spanv-show="finished">没有更多了</span><spanv-show="loadError"@click="handleLoadMore">请求失败,点击重新加载</span></div></template></DynamicScroller></template><van-emptyv-elseimage="./no_data.png"description="暂无匹配数据":image-size="['60vw', 'auto']"class="h-80vh"/></van-pull-refresh></div><van-back-toptarget="#virtual-scroller"/></template>
// 下拉loadingconst refreshLoading = ref<boolean>(false)// 禁用下拉刷新const disabledPullRefresh =ref(false)// 上拉loadingconst loadMoreLoading = ref<boolean>(false)// 没有更多数据了const finished = ref<boolean>(false)// 加载失败const loadError = ref<boolean>(false)// 实现上拉加载consthandleDynamicScrollerScroll=(e: Event)=>{// 距顶部const scrollTop =(e.target as HTMLDivElement)?.scrollTop ||0// 可视区高度const clientHeight =(e.target as HTMLDivElement).clientHeight
// 滚动条总高度const scrollHeight =(e.target as HTMLDivElement)?.scrollHeight
const offset =300// 如果直接用 `vant PullRefresh` 包裹虚拟滚动,会导致无法向下滑动,任何位置下拉都会触发下拉刷新。// 控制是否开启下拉刷新if(scrollTop <=4){
disabledPullRefresh.value =false}else{
disabledPullRefresh.value =true}// 如果当前距离顶部的值加上可视区域的值大于等于总高度,则滚动条触底if(scrollTop + clientHeight >= scrollHeight - offset){if(!loadMoreLoading.value &&!finished.value &&!loadError.value){
console.log('滚动到底部了')
loadMoreLoading.value =truehandleLoadMore()}}}
兄弟们,上面的代码在项目中踩坑实测过了!!
源码就不贴了😄
版权归原作者 __畫戟__ 所有, 如有侵权,请联系我们删除。