向歆纪

虚拟滚动

有 N 人看过

什么是虚拟滚动: 我的定义是通过一种操作可以让一个滚动容器内的子元素可以不用把数据全部渲染出来, 就是指渲染出可视部分

在滚动上确实是有很多方式去实现一些性能优化, 比如说懒加载, 底部加载,虚拟滚动….

拿懒加载来说, 优化的方式也是在用户可以看见的部分去动态的加载某些资源或者组件, 比如图片等 , 这个是一种

比起底部加载也差不多,如果说普通的列表是分页, 那么底部加载就更适合移动端去做处理. 用户滑动到最下方再向服务器发起请求, 获取新数据, 然后再进行追加

那虚拟滚动和上面两种方案也差不多


场景: 后台数据量很大, 如果全部渲染到页面上会导致 dom 节点过多而卡顿或者卡死, 在这个时候我们就要去控制一个容器里面的节点元素不能太多 , 这个也是虚拟滚动的意义

传统方式 : List 不论有多少, 统统都渲染出来

img.png

虚拟滚动 : 只渲染可视区域的 List Item

img.png


  1. 准备好一个模拟高度的元素
  2. 确定好每个元素的大小
  3. 确定好需要渲染的长度 / 确定好可视区域的宽高
  4. 确定好可视区域的 list

首先我们得模拟一个元素的高度, 也是因为这个我们要让这个容器弄出来一个虚拟的滚动条, 而且下面的元素全部都使用绝对定位

假设我这要渲染的数据长度为 50 , 那这个虚拟的元素高度为 50 * 50


<div class="mock-bar" :style="{height : data.length * 50 + 'px'}"></div>

每一个元素渲染的位置 : item.top = 当前元素索引 * 50 , 相当于提前计算好他们所展示的位置


<div class="list-item" v-for="item in sliceData" :key="item.value"
     :style="{ top : (item.top) + 'px' }">
{{ item.value }}
</div>

到这里第一步和第二部就做好了 , 然后我们要去确定范围, 我们可以去通过 scroll 事件监听这个元素滚动到了哪里

const state = reactive({
    start: 0,  // 规定 list 的渲染索引 
    length: 15, // 已经他的渲染长度 
})

然后通过 scroll 事件去执行计算方法, 算出 start 和 length

Container 容器


<div class="container" @scroll="onContainerScroll">
<!--    ...-->
</div>

使用了节流函数 , 并且获取 scroll top , 去调用 computedDataScope 去计算需要展示的 list

sliceData 才是真实渲染的 List 数组, 相当于每次都是在截取 全部数组中的一部分, 展示出来

function computedDataScope(top: number) {
    state.start = parseInt(top / 50)
    sliceData.value = data.value.slice(state.start, state.start + state.length)
}

const onContainerScroll = throttle(function (event: Event) {
    const target = event.target as HTMLElement
    const t = target.scrollTop
    state.scope.top = t
    computedDataScope(t)
}, 100)
computedDataScope(0)

Template


<div class="list-item" v-for="item in sliceData" :key="item.value"
     :style="{ top : (item.top) + 'px' }">
    {{ item.value }}
</div>