前端长列表优化-虚拟列表
什么是长列表?
前端的业务开发中会遇到一些数据量较大且无法使用分页方式来加载的列表,我们一般把这种列表叫做长列表。
完整渲染的长列表基本上很难达到业务上的要求的,非完整渲染的长列表一般有两种方式:
- 懒渲染:这个就是常见的无限滚动的,每次只渲染一部分(比如 10 条),等剩余部分滚动到可见区域,就再渲染另一部分。
- 可视区域渲染:只渲染可见部分,不可见部分不渲染。
虚拟列表就是采用的可视区渲染方式优化。
虚拟列表实现原理?
虚拟列表(Virtual List),是一种长列表优化方案,是可视区渲染列表。其两个重要的概念:
- 可滚动区域:假设有 1000 条数据,每个列表项的高度是 30,那么可滚动的区域的高度就是 1000 * 30。当用户改变列表的滚动条的当前滚动值的时候,会造成可见区域的内容的变更。
- 可见区域:比如列表的高度是 300,右侧有纵向滚动条可以滚动,那么视觉可见的区域就是可见区域。
虚拟列表原理:
用数组保存所有列表元素的位置,只渲染可视区内的列表元素,当可视区滚动时,根据滚动的offset大小以及所有列表元素的位置,计算在可视区应该渲染哪些元素。
实现
实现虚拟列表就是处理滚动条滚动后的可见区域的变更,具体实现步骤如下
- 计算当前可见区域起始数据的 startIndex
- 计算当前可见区域结束数据的 endIndex
- 计算当前可见区域的数据,并渲染到页面中
- 计算 startIndex 对应的数据在整个列表中的偏移位置 startOffset,并设置到列表上
做了一个设定:每个列表项的高度都是 30px。在这个约定下,核心 JavaScript 代码不超过 10 行,但是可以完整的实现可见区域的渲染和更新。
HTML、CSS 如何实现,添加了这么几个样式:
- 列表元素(.list-view)使用相对定位
- 使用一个不可见元素(.list-view-phantom)撑起这个列表,让列表的滚动条出现
- 列表的可见元素(.list-view-content)使用绝对定位,left、right、top 设置为 0
.list-view { height: 400px; overflow: auto; position: relative; border: 1px solid #aaa; } .list-view-phantom { position: absolute; left: 0; top: 0; right: 0; z-index: -1; } .list-view-content { left: 0; right: 0; top: 0; position: absolute; } .list-view-item { padding: 5px; color: #666; line-height: 30px; box-sizing: border-box; }
HTML 代码如下(先忽略其中的事件、变量绑定):
<template> <div class="list-view" @scroll="handleScroll"> <div class="list-view-phantom" :style="{ height: contentHeight }"> </div> <div ref="content" class="list-view-content"> <div class="list-view-item" :style="{ height: itemHeight + 'px' }" v-for="item in visibleData"> {{ item.value }} </div> </div> </div> </template>
JavaScript 代码如下:
export default { name: 'ListView', props: { data: { type: Array, required: true }, itemHeight: { type: Number, default: 30 } }, computed: { contentHeight() { return this.data.length * this.itemHeight + 'px'; } }, mounted() { this.updateVisibleData(); }, data() { return { visibleData: [] }; }, methods: { updateVisibleData(scrollTop) { scrollTop = scrollTop || 0; const visibleCount = Math.ceil(this.$el.clientHeight / this.itemHeight); const start = Math.floor(scrollTop / this.itemHeight); const end = start + visibleCount; this.visibleData = this.data.slice(start, end); this.$refs.content.style.webkitTransform = `translate3d(0, ${ start * this.itemHeight }px, 0)`; }, handleScroll() { const scrollTop = this.$el.scrollTop; this.updateVisibleData(scrollTop); } } }
- 渲染可见区域的数据是 Vue.js 完成的,使用的是 visibleData 这个数组中的元素
- 其中虚拟列表的更新逻辑是 updateVisibleData 这个方法在上面代码已经加了注释
- 为了让虚拟列表可以正确的出现滚动条,使用了 Vue.js 的计算属性 contentHeight 来计算滚动区域的真正高度
版权声明:
作者:wuhou123
链接:https://wuhou.fun/468.html
来源:前端网
文章版权归作者所有,未经允许请勿转载。

共有 0 条评论