长列表
是什么?
开发中遇到的数据量较大且无法使用分页方式来加载的列表,这种列表称为长列表。
非完整渲染的长列表一般有两种方式:
- 懒加载:每次只渲染一部分,等剩余部分滚到可见区域,就再渲染一部分。
- 可视区域渲染:只渲染可见部分,不可见部分不渲染。
虚拟列表的原理
使用数组保存所有列表元素的位置,只渲染可视区内的列表元素,当可视区滚动时,会计算在可视区内应该渲染哪些元素。
参考实现
实现虚拟列表就是处理滚动条滚动后的可见区域的变更。具体实现步骤:
- 计算当前可见区域起始数据的startIndex
- 计算当前可见区域结束数据的endIndex
- 计算当前可见区域的数据,并渲染到页面中
- 计算startIndex对应的数据在整个列表中的偏移位置startOffset,并设置到列表上
如下实现中,规定:
- 列表元素(list-view)使用相对定位
- 使用不可见元素(list-view-phantom)撑起这个列表,让列表的滚动条出现
- 列表的可见元素(list-view-content)使用绝对定位,left \ top \ right设置为0
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| <style> .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; } </style>
|
1 2 3 4 5 6 7 8 9 10 11
| <body> <div class="list-view" ref="scrollBox" @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}} </div> </div> </div> </body>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| contentHeight(){ return this.data.length * this.itemHeight+'px'; } updateVisibleData(scrollTop=0){ // 当前可视区可以放置的列表条数 const visibleCount = Math.ceil(this.$refs.scrollBox.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.$refs.scrollBox.scrollTop; this.updateVisibleData(scrollTop); }
|