问题
长列表数据渲染,采用分页加载,功能实现,所带来的的问题是节点在不断的添加,增加浏览器性能压力
我google很久找了一个解决方案,自己动手copy一份vue的demo
<template>
<div class="main">
<div class="head"></div>
<div class="content">
<div class="infinite-list-wrapper" ref="wrapper" @scroll="scrollHandler">
<div class="infinite-list-ghost" :style="{ height: contentHeight + 'px' }"></div>
<div class="infinite-list" :style="{ transform: `translate(0, ${top}px)` }">
<div
class="item"
v-for="(item, index) in visibleData"
:key="index"
:style="{ height: `${item.height}px`, lineHeight: `${item.height}px` }">
{{ `item-${item.val}` }}
</div>
</div>
</div>
</div>
</div>
</template>
<script>
const randomBoolean = () => Math.random() - 0.5 > 0
const list = []
for (let val = 0; val < 777; val++) {
// randam height
const height = randomBoolean() ? 60 : 30
const obj = { val, height: 60 }
if (!val) {
obj.offsetTop = height
}
list.push(obj)
}
const contentHeight = list.reduce((p, c) => p + c.height, 0)
export default {
data () {
return {
// 可视区域top
top: 0,
// 可见高度
visibleHeight: 0,
// 可见列表
visibleData: [],
// 上下预加载个数
offset: 10,
// 间隔
interval: 2,
// 总列表,及offsetTop
list,
// 数据总高度
contentHeight
}
},
mounted () {
/*
1. 获取当前盒子的可视区高度
2. 获得初始化数据
*/
this.$nextTick(() => {
const wrapper = this.$refs.wrapper
wrapper.onscroll = this.scrollHandler
})
const visibleHeight = this.$refs.wrapper.clientHeight
this.visibleHeight = visibleHeight
const { visibleData, top } = this.doCalculate(0)
this.visibleData = visibleData
this.top = top
},
methods: {
scrollHandler (e) {
const startIndex = this.findStartIndex(e.target.scrollTop)
if (startIndex % this.interval === 0) {
const { visibleData, top } = this.doCalculate(startIndex)
this.visibleData = visibleData
this.top = top
}
},
findStartIndex (top) {
let index = 0
while (index < this.list.length) {
if (!this.list[index].offsetTop) {
this.calculateOffset(index)
}
if (top < list[index].offsetTop) {
break
}
index++
}
return index
},
calculateOffset (index) {
if (index === this.list.length) {
return
}
// 取缓存
if (this.list[index].offsetTop) {
return this.list[index].offsetTop
}
let offsetTop = this.list[index].height
// console.log('1111----:::::', index - 1)
offsetTop = offsetTop + this.calculateOffset(index - 1)
// console.log('offsetTop::::', offsetTop)
// 添加缓存
this.list[index] = {
...this.list[index],
offsetTop
}
// console.log(this.list)
return offsetTop
},
doCalculate (startIndex) {
/*
预加载数 * 2 + 1 + endIndex = 新的endIndex
*/
const innerOffset = startIndex = startIndex - this.offset
// console.log('doCalculate::::', innerOffset)
startIndex = startIndex > 0 ? startIndex : 0
let endIndex = this.findEndIndex(startIndex) + this.offset * 2 + 1
endIndex = innerOffset < 0 ? endIndex + innerOffset : endIndex
endIndex = endIndex > this.list.length ? this.list.length : endIndex
const visibleData = this.list.slice(startIndex, endIndex)
// console.log('doCalculate:::::-endIndex', endIndex)
const top = this.findTopByIndex(startIndex)
return {
visibleData,
top
}
},
findTopByIndex (index) {
return index ? this.list[index - 1].offsetTop : 0
},
findEndIndex (startIndex) {
if (this.list[startIndex].endIndex) {
return this.list[startIndex].endIndex
}
let visibleHeight = this.visibleHeight || this.$refs.wrapper.clientHeight
const endIndex = this.calculateEndIndex(visibleHeight, startIndex)
this.$set(this.list[startIndex], 'endIndex', endIndex)
return endIndex
},
calculateEndIndex (visibleHeight, index = 0) {
while (visibleHeight > 0) {
const i = index + 1
if (i !== this.list.length) {
visibleHeight = visibleHeight - this.list[++index].height
} else {
break
}
}
return index
}
}
}
</script>
<style>
* {
margin:0;
padding: 0;
}
html,body,#app {
height: 100%;
}
.main{
width:100%;
height: 100%;
display: flex;
flex-direction: column;
}
.head{
width:100%;
height: 30px;
background: red;
}
.content{
flex:1;
overflow: hidden;
}
.infinite-list-wrapper {
width: 100%;
height: 100%;
overflow-y: auto;
position: relative;
}
.infinite-list-ghost {
position: absolute;
right: 0;
width: 1px;
z-index: -1;
}
.infinite-list {
overflow: auto;
width: 100%;
}
.infinite-list .item {
text-align: center;
line-height: 30px;
}
.infinite-list .item:nth-child(odd) {
background-color: #eee;
}
</style>