前端处理超长列表-列表虚拟加载
*本文参考https://mp.weixin.qq.com/s/3euILxqRsxkOCvK6fBu2xg里面讲述的比较详细可选方案1.懒加载(上拉加载)通过懒加载的方式,在出现长列表的时候,第一次并不完全渲染所有的DOM节点,即可解决一部分场景下的问题。优点:实现简单缺点:1.想要定位到某一个位置的数据会非常困难1.如果一直加载到底,那么最终还是会出现大量的DOM节点,导致滚动不流畅2.虚拟渲
·
*本文参考https://mp.weixin.qq.com/s/3euILxqRsxkOCvK6fBu2xg里面讲述的比较详细
可选方案
1.懒加载(上拉加载)
通过懒加载的方式,在出现长列表的时候,第一次并不完全渲染所有的DOM节点,即可解决一部分场景下的问题。
优点:实现简单
缺点:
1.想要定位到某一个位置的数据会非常困难
1.如果一直加载到底,那么最终还是会出现大量的DOM节点,导致滚动不流畅
2.虚拟渲染
第三方库
vue-virtual-scroller、react-tiny-virtual-list、react-virtualized
2.1 原理
它们的实现原理是利用视差和错觉制作一份出一份“虚拟”列表,一个虚拟列表由三部分组成(拿到列表总高度,通过滚动条滚动的距离来动态计算所展示的列表)
1.视窗口(用户所看到的区域)
2.虚拟数据列表(数据展示)
3.滚动占位区块(底部滚动区)
下面实现一个简单的虚拟渲染
HTML
<!-- 虚拟滚动 -->
<div class="virtual-scroll" @scroll.passive="onScroll">
<div class="over-hide"
:style="{
'height': `${data.length * itemHeight}px`
}">
<ul :style="${'margin-top': `${scrollTop}px`}" class="virtual-box">
<li class="virtual-item"
v-for="i in virtualData"
:key="i">
{{i.item}}
</li>
</ul>
</div>
</div>
JS
export default {
data() {
return {
scrollTop: 0, // 滚动的距离
itemHeight: 40, // 每个列表的高度
startIndex: 0, // 开始截取的下标
endIndex: 10, // 结束截取的下标
totalLength: 10000, // 列表总长度
viewLength: 10 // 当DOM渲染的数量
}
},
computed: {
data() {
const list = [...Array(this.totalLength || 0).keys()].map(i => ({
item: `列表${i}`,
key: i
}))
return list
},
virtualData() {
return this.data.slice(this.startIndex, this.endIndex)
}
},
methods: {
onScroll(e) {
let scrollTop = e.target.scrollTop
this.startIndex = Math.floor(scrollTop / this.itemHeight)
this.scrollTop = this.startIndex * this.itemHeight
this.endIndex = this.startIndex + this.viewLength
}
}
}
CSS
.virtual-scroll {
width: 300px;
height: 200px;
overflow-y: auto;
}
.over-hide {
overflow:hidden;
}
.virtual-box {
padding: 0;
margin: 0;
border: 1px solid #e5e5e5;
list-style: none;
}
.virtual-item {
box-sizing: border-box;
width: 100%;
height: 40px;
line-height: 40px;
border-bottom: 1px solid #e5e5e5;
}
扩展另一种写法
HTML
<!-- 虚拟滚动 -->
<div class="virtual-scroll" @scroll.passive="onScroll">
<div class="over-hide"
:style="{
'padding': `${scrollTop}px 0 ${data.length * itemHeight - overTop}px`
}">
<ul class="virtual-box">
<li class="virtual-item"
v-for="i in virtualData"
:key="i">
{{i.item}}
</li>
</ul>
</div>
</div>
JS
export default {
data() {
return {
scrollTop: 0, // 滚动的距离
itemHeight: 40, // 每个列表的高度
startIndex: 0, // 开始截取的下标
endIndex: 10, // 结束截取的下标
totalLength: 10000, // 列表总成度
viewLength: 10, // 当DOM渲染的数量
overTop: 0, // 当前滚动的高度+++++++++++++++++++++++
}
},
computed: {
data() {
const list = [...Array(this.totalLength || 0).keys()].map(i => ({
item: `列表${i}`,
key: i
}))
return list
},
virtualData() {
return this.data.slice(this.startIndex, this.endIndex)
}
},
methods: {
onScroll(e) {
let scrollTop = e.target.scrollTop
this.startIndex = Math.floor(scrollTop / this.itemHeight)
this.endIndex = this.startIndex + this.viewLength
this.scrollTop = this.startIndex * this.itemHeight
this.overTop = this.startIndex * this.itemHeight // ++++++++++++++++++++
if (this.endIndex > this.data.length) {
this.overTop = this.data.length * this.itemHeight
this.endIndex = this.data.length
}
}
}
}
频繁触发滚动事件优化
onScroll(e) {
let scrollTop = e.target.scrollTop
let startIndex = Math.floor(scrollTop / this.itemHeight)
let endIndex = startIndex + this.viewLength
let currentScrollTop = startIndex * this.itemHeight // 当前滚动高度
let scroll = this.itemHeight * (this.viewLength - 1) // // 滚动多少距离后 改变位置和列表
// 如果往下滚了可视区域的一部分,或者往上滚任意距离
if (currentScrollTop - this.scrollTop > scroll || currentScrollTop - this.scrollTop < 0) {
this.scrollTop = currentScrollTop
this.startIndex = startIndex
this.endIndex = endIndex
this.overTop = currentScrollTop
if (endIndex > this.data.length) this.overTop = this.data.length * this.itemHeight
}
}
更多推荐
已为社区贡献1条内容
所有评论(0)