对于长列表这种情况,我们在项目中最常见的性能优化方式有哪些呢?今天笔者正好有时间,那么我就来说一些我知道的吧!以Vue为例,笔者太low 目前还不会react,请各位看官见谅哈。
Object.freeze()
对于不需要修改列表的数据,我们可以通过Object.freeze()来阻止Object.defineProperty()对于数据的响应式劫持
var data = {a:1,b:2,c:{d:3},e:[1,2,3]}
function isObject(data){
return data !== null && typeof data === "object"
}
function observe(data){
if(!isObject(data)) return
if( Array.isArray(data) ){
// 函数劫持 对数组的原生方法进行劫持
}else{
Object.keys(data).forEach(key=>{
defineReactive(data, key, data[key])
})
}
}
function defineReactive(data, key, value){
observe(value) //递归劫持
let dep = new Dep()
Object.defineProperty(data, key, {
get(){
if(Dep.target){
dep.depend() // 订阅watcher
}
return value
},
set(newValue){
if( value === newValue ) return
observe(newValue) //递归劫持
value = newValue
dep.notify() // 触发更新
}
})
}
下拉刷新
这就类似于分页功能, 每次下拉触底page++。但这个需要注意一点,有个类似锁的概念,避免出现死锁
getData(){
if(this.loading) return
this.loading = true;
axios.get('/data', {
params: {
page:page
}
})
.then(function (response) {
this.loading = false
console.log(response);
if(page*size + size > total){
this.loading = true
}
})
.catch(function (error) {
this.loading = false
console.log(error);
});
}
虚拟列表
虚拟列表的主要思路就是动态的维护页面上现有的DOM节点,以保证节点数量来保证页面的性能。现有的插件vue-virtual-scroller
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
*{
margin:0;
padding:0;
}
.list-wrap{
position: relative;
overflow-y: scroll;
width: 200px;
margin: 100px auto;
box-sizing: border-box;
border: solid 1px red;
}
.list{
width: 100%;
position: absolute;
top: 0;
left: 0;
}
.list li{
width: calc(100% - 20px);
padding-left:20px;
border-bottom: solid 1px blue;
height: 29px;
line-height: 29px;
}
</style>
</head>
<body>
<ul id="app">
<div class="list-wrap" ref="listWrap" @scroll="scrollListener">
<div class="scroll-bar" ref="scrollBar"></div>
<ul class="list" ref="list">
<li v-for="val in showList">{{val}}</li>
</ul>
</div>
</ul>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.3/vue.js"></script>
<script>
new Vue({
el: '#app',
data(){
return {
list: [],//全部显示数据
itemHeight: 30,//每一列的高度
showNum: 10,//显示几条数据
start: 0,//滚动过程显示的开始索引
end: 10,//滚动过程显示的结束索引
timer: 0 //节流
}
},
computed: {
//显示的数组,用计算属性计算
showList(){
return this.list.slice(this.start, this.end);
}
},
mounted(){
//长列表
for (let i = 0; i < 50; i++) {
this.list.push('列表' + i)
}
//计算滚动容器高度
this.$refs.listWrap.style.height = this.itemHeight * this.showNum + 'px';
//计算总的数据需要的高度,构造滚动条高度
this.$refs.scrollBar.style.height = this.itemHeight * this.list.length + 'px';
},
methods: {
scrollListener(){
if(this.timer) return
this.timer = setTimeout( _ =>{
console.log("scroll")
//获取滚动高度
let scrollTop = this.$refs.listWrap.scrollTop;
//开始的数组索引
this.start = Math.floor(scrollTop / this.itemHeight);
//结束索引+1 解决第一个显示一部分 下面会空白一点
this.end = this.start + this.showNum + 1;
//绝对定位对相对定位的偏移量
// this.$refs.list.style.top = this.start * this.itemHeight + 'px';
this.$refs.list.style.transform = `translateY(${this.start * this.itemHeight}px)`; //GPU加速
this.timer = 0;
}, 50)
}
}
})
</script>
</body>
</html>