当前位置: 首页 > Web前端 > HTML

长列表渲染优化——虚拟列表

时间:2023-03-28 17:40:17 HTML

.container{宽度:100vw;高度:100%;溢出:自动;位置:相对;}.phantom{位置:绝对;左:0;顶部:0;右:0;z-index:-1;}.list{left:0;右:0;顶部:0;位置:绝对;text-align:center;}.list-item{padding:10px;-底部:1px纯黑色;背景:对于长列表的渲染,一般采用分页或者懒加载的方式,将数据下拉到底部再向后端请求。每次只加载一部分数据,但随着加载的数据越来越多。页面的Dom无限增加,会给浏览器带来负担,整个滑动也会出现卡顿。解决方案:虚拟列表虚拟列表实际上是一种按需展示的表现形式。只渲染可见区域,不渲染或部分渲染非可见区域数据,减轻浏览器负担,提高渲染性能。第一次渲染时,可见区域的高度÷单个列表项的高度=一屏需要渲染的列表个数。当发生滚动时,记录滚动距离。根据滚动距离和单个列表项的高度,可以知道当前可见区域的索引。同时,为了营造滚动效果,在列表区,将transform属性的translate的Y值设置为scrollTop-(scrollTop%itemSize)(当滚动到数据项中间时,y值oftransform不包含数据项)总结:虚拟列表的实现其实就是在首屏加载的时候只加载可见区域需要的列表项。当发生滚动时,通过计算动态获取可见区域的列表项,非可见区域内存中的列表项被移除。Dom不改变,数据改变。避免分页和懒加载会无限增加Dom的缺点。两种场景的具体实现:1.定高场景(1)首先确定DOM结构:第一层作为容器,作为容器层。功能:监听滚动并记录滚动位置。第二层scrollTop分为placeholder层和list层。列表层为可视化区域,渲染列表区域,使用translate3d显示动画滚动效果,其中y值与容器层记录的滚动位置相关。(2)父组件传入所有列表数据,以及每个列表项的高度。(3)可以计算出整个列表的长度,给占位层的高度赋值。数据长度*单个列表项的高度(4)计算可见区域的高度,计算一屏可以显示的列表数。定义两个变量start和end来控制可见区域的起始索引和结束索引。通过起始索引和结束索引更新可视区域列表数据。(5)监听容器滚动,记录滚动位置scrollTop,并更新列表区域scrollTop的开始、结束、偏移量-(scrollTop%单个列表项的高度).container{宽度:100vw;高度:100%;溢出:自动;位置:相对;}.phantom{位置:绝对;左:0;顶部:0;右:0;z-index:-1;}.list{left:0;右:0;顶部:0;位置:绝对;text-align:center;}.list-item{padding:10px;-底部:1px纯黑色;2.在变高场景之前的定高场景中,可以根据可见区域的高度和单个列表项的高度来准确计算需要渲染的列表数量。但是在实际应用中,很多列表项的高度可能不是固定的。解决虚拟列表高度可变的情况一般有三种方案:(1)扩展组件的itemSize属性可以支持数字、数组、函数。但前提是你需要知道每个列表的高度;(2)渲染可视区域外的列表项,测量并缓存高度,然后渲染到可视区域。但是渲染成本成倍增加,这是不可行的;(3)使用估计高度。更新页面时,记录每个列表项的真实高度和位置信息。由于方案一和方案二可行性不高,这里采用方案三。定义组件属性estimatedItemSize,用于接收预估高度;定义position,用于存储列表项渲染后每一项的高度和位置信息;初始化位置;有索引、高度、顶部、底部值;initPositions(){this.positions=this.listData.map((item,index)=>{return{index,height:this.estimatedItemSize,top:index*this.estimatedItemSize,bottom:(index+1)*this.estimatedItemSize}})}计算占位层的高度listHeight(){returnthis.position[this.positions.length-1].bottom;}渲染完成后,在update中获取每个list的高度和位置信息并将它们存储在位置上;updated(){letnodes=this.$refs.items;nodes.forEach(node=>{letrect=node.getBoundingClientRect();letheight=rect.height;letindex=+node.id.slice(1);letoldHeight=this.positions[index].height;//计算估计高度和实际高度的差值letdValue=oldHeight-height;if(dValue!==0){//更新元素的高度和底部this.positions[index].height=height;这个.positions[index].bottom=this.positions[index].bottom-dValue;//因为高度发生变化,需要更新元素后面的top和bottom;对于(letk=index+1;kValue的情况下,用一个tempIndex来记录。end向左移动一位。返回tempIndex的值。binarySearch(list,value){让开始=0;letend=list.length-1;让tempIndex=null;while(start<=end){letmiddle=start+Math.floor(end-start);让middleValue=list[middle].bottom;if(middleValue===value){//因为bottom作为引用,返回的是list的起始索引,需要+1returnmiddle+1;}elseif(middleValuemidIndex){tempIndex=middleIndex;}结束=结束-1;}}returntempIndex;},更改滚动后获取偏移量的方法scrollEvent(){//....if(this.start>=1){this.startOffset=this.positions[this.start-1]。底部;}else{this.startOffset=0;}}其他方案:对于长列表的优化,已经有比较成熟的方案。在react中,react-virtualized和react-window比较优秀。他们的核心方法仍然是虚拟列表。react-virtualized:https://www.jianshu.com/p/fc9...使用提供的List组件,设置组件的宽高,渲染总行数rowCount,每个列表卡片的高度rowHeight,以及每个列表卡片的渲染函数rowRende。参考资料:https://juejin.cn/post/684490...