概述
- 属于专栏-项目难点
- 重要程度:★★★★☆
背景
当我们遇到一个列表页面,数据量特别大(例如数据量为1万条数据),并且页面采用是滑动上拉分页时,用户会感觉到页面滑动卡顿、数据渲染较慢的问题,这个时候需要对长列表进行性能优化
思路
可以通过按需进行加载dom,即只显示可视化区域的数据,不在可视区域的数据只做dom的占位处理,不显示数据,从而减少减少dom的结构, 实现性能提升
实现
方式1-不封装组件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.14/vue.js"></script>
</head>
<style>
.sroll-box{
box-sizing: border-box;
overflow: auto;
border: 1px solid #666;
}
.scroll-item{
border-bottom: 1px solid #666;
box-sizing: border-box;
}
</style>
<body>
<div id="app">
<div class="sroll-box" ref="box" @scroll="handleScroll" :style="{
height: visibleCount*itemHeight + 'px'
}">
<div class="scroll-list" :style="{
height: list.length * itemHeight + 'px', transform: `translateY(${fixedScrollTop}px)`
}">
<div class="scroll-item" v-for="item in visibleData" :style="{height:itemHeight + 'px', 'line-height': itemHeight + 'px'}">
{{item.value}}
</div>
</div>
</div>
</div>
</body>
<script>
new Vue({
el: '#app',
data(){
//生成数据
let list = []
for(var i=0;i<500;i++){
list.push({id:i,value:i+1})
}
return {
list, //数据列表
itemHeight: 30,//每条记录的高度
visibleCount: 10,//可视化区域,渲染个数
start: 0, //可视区域,渲染数据开始索引值
end: 0,//可视化区域,渲染数据结束索引值
visibleData: [], //可视化区域渲染的数据列表
fixedScrollTop: 0//偏移高度
}
},
mounted() {
// 可视化高度
let clientHeight = this.$refs.box.clientHeight
//初始化数据
this.visibleCount = Math.ceil(clientHeight / this.itemHeight)
this.start = 0
this.end = this.start + this.visibleCount
this.visibleData = this.list.slice(this.start, this.end)
},
methods: {
handleScroll(){
const scrollTop = this.$refs.box.scrollTop;//获取 dom 滚动高度
const offsetNumber = Math.floor(scrollTop / this.itemHeight); //计算偏移个数
this.fixedScrollTop = offsetNumber * this.itemHeight; //计算偏移高度
//计算开始 结束 数据
this.start = offsetNumber;
this.end = this.start + this.visibleCount;
this.visibleData = this.list.slice(this.start, this.end);
}
}
})
</script>
</html>
预览
方式2-封装组件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.14/vue.js"></script>
</head>
<style>
.sroll-box{
box-sizing: border-box;
overflow: auto;
border: 1px solid #666;
}
.scroll-item{
border-bottom: 1px solid #666;
box-sizing: border-box;
}
</style>
<body>
<div id="app">
<virtual-list :list="list" :item-height="40" :visible-count="15"></virtual-list>
</div>
</body>
<script>
//组件virtual-list
Vue.component('virtual-list', {
template: `
<div class="sroll-box" ref="box" @scroll="handleScroll" :style="{
height: visibleCount*itemHeight + 'px'
}">
<div class="scroll-list" :style="{
height: list.length * itemHeight + 'px', transform: 'translateY('+ fixedScrollTop+ 'px)'
}">
<div class="scroll-item" v-for="item in visibleData" :style="{height:itemHeight + 'px', 'line-height': itemHeight + 'px'}">
{{item.value}}
</div>
</div>
</div>
`,
props: {
list: {
type: Array,//数据列表
default: []
},
itemHeight: {//每条记录的高度
type: Number,
default: 30
},
visibleCount: {//可视化区域,渲染个数
type: Number,
default: 10
}
},
data(){
return {
start: 0, //可视区域,渲染数据开始索引值
end: 0,//可视化区域,渲染数据结束索引值
visibleData: [], //可视化区域渲染的数据列表
fixedScrollTop: 0//偏移高度
}
},
mounted() {
// 可视化高度
let clientHeight = this.$refs.box.clientHeight
//初始化数据
this.visibleCount = Math.ceil(clientHeight / this.itemHeight)
this.start = 0
this.end = this.start + this.visibleCount
this.visibleData = this.list.slice(this.start, this.end)
},
methods: {
handleScroll(){
const scrollTop = this.$refs.box.scrollTop;//获取 dom 滚动高度
const offsetNumber = Math.floor(scrollTop / this.itemHeight); //计算偏移个数
this.fixedScrollTop = offsetNumber * this.itemHeight; //计算偏移高度
//计算开始 结束 数据
this.start = offsetNumber;
this.end = this.start + this.visibleCount;
this.visibleData = this.list.slice(this.start, this.end);
}
}
})
new Vue({
el: '#app',
data(){
//生成数据
let list = []
for(var i=0;i<500;i++){
list.push({id:i,value:i+1})
}
return {
list
}
}
})
</script>
</html>
预览
其他实现
可以使用npm中实现这个功能的第三方包,比如lang-virtual-list-vue、vue-virtual-scroll-list、vue-virtualized-list