记一笔虚拟列表渲染理解

之前也有被问或者听说 如果给你一万条数据,你怎么渲染?,当时觉得为什么会这么问,不是有分页或者上拉加载吗?

分页和上拉加载确实能解决这个问题,不过这和虚拟列表是两码事。

解释: 不管是分页,还是上拉加载,有一万条数据其实就会渲染一万条dom,dom过多无疑对滚动的流畅性或性能都有一定的影响。

区别: 虚拟列表在于,哪怕有一万条数据,列表中只会渲染一部分dom(通常是正常可见的部分dom)。相信看到这里应该就明白不同之处了吧!

过程分析:
在这里插入图片描述
如上图,这是一个正常列表的模型,当我们往上滑动时,dom就会不断加载。对于用户而言,可视的区域始终只有6个(如:第0个~第5个),其他的,你渲染了,也是白渲染,所以我们可以只渲染可视6个(为了流畅点,肯定不至于这么死板,一般多固定渲染一部分)

问题一:页面布局细节问题

由于我们只只会渲染 8(举例8个) 个dom,但是我们的列表又要1w条的高度,还要滚动条,看起来才像真的。
因此,通常会计算出滚动dom高度

布局

<h3 class="title" >列表头部</h3>
   <div class="v-list-box" @scroll="onListScroll" >
       <div class="v-list" :style="vHeight"  >
           <div v-for="item in renderArr" :style="item.style"  class="item" >
               {
   
   {item.text}}
           </div>
       </div>
   </div>
<h3 class="title" >列表尾部</h3>

高度计算

computed: {
    
    
    // 计算 v-list 本身高度
    vHeight( {
    
     arr } ) {
    
    
        return {
    
    
            height: arr.length * 50 + 'px',
        }
    }
},

问题二:数组分割问题

对于数据,通常是数组,那么我们可以考虑把数组分割成(假设始终只渲染8个,那么我们可以按长度为8来分割数组)

arrPipe(start = 0,end = start + 8) {
    
    
   let pipe = this.arr.slice(start,end)
   this.renderArr = pipe
},

问题三:更新对应区间数据

既然拿到了分块的数据,那么我们只需要在每次滚动后,就将对应区间的数据再次渲染上去,因此我们还需要监听下滚动事件,根据滚动的高度,进而判断滚动了几个dom,进而分割对应区间数据

onListScroll( {
    
     target: {
    
     scrollTop } } ) {
    
    
    // 计算切片 假设dom高度固定为50
    let start = Math.floor(scrollTop / 50)
    // 小优化,只有当 上一个dom消失在可视区域,才进行更新
    if(start != this.lastStart) {
    
    
        this.lastStart = start
        this.arrPipe(start)
    }
},

细节问题

按照上面的步骤,可以看到滚动时,dom能够及时更新,但是位置没有及时更替,始终在初始位置,因为我们只渲染了8个dom,所以并不会一次排列下来,所以需要对这些dom的位置进行位移计算

// 在渲染时,再次计算下对应的位置关系
style: {
    
     
   	transform: `translateY(${
      
      v * 50}px)`,
     position: 'absolute',
     width: '100%'
}

结果如下:
在这里插入图片描述
总结:上述只是对虚拟列表实现方案的简单探索,优化的地方还有很多(还可以将每次消失的dom位移到下一个出现的位置,减少渲染)。现在已经有很多插件可以用,还支持不同的高度的dom渲染

如:vue-virtual-scroller(三方插件)、useVirtualList(VueUse)

猜你喜欢

转载自blog.csdn.net/qq_45219069/article/details/123647886