我是歌谣 放弃很容易 但是坚持一定很酷 微信公众号关注前端小歌谣 带你进入巅峰前端交流群

前言

最近没啥项目做,闲着无聊,写写文档解解闷。

也许看到这标题的时候,开发肯定想捶提供数据的后台小哥,数据量这么大,就不能做下分页在返回嚒。直接渲染到页面会引起页面的卡顿,白屏时间过长会造成用户的流失。虽然心中有无数匹马在狂奔,但是在一些场景下,还是有可能你拿到的数据就是这样的。

正文

提出问题:

1、十万条数据要如何进行处理?

2、直接渲染页面会造成什么问题?

3、如何提升整体页面的操作性能?

解决方案:

分页 + 懒加载 + 防抖函数

话不多说,先造个demo先,demo如下:

前端入门到弃坑:10000条数据怎么优化_javascript

上面显示毫秒分别是造数据花费的时间和页面未渲染数据所花费的时间,方便后续统计优化前后的区别。

首先我们要先造测试数据,code如下:

createData() {
this.userList = [];
for (let i = 0; i < 100000; i++) {
((i) => {
let user = {
name: "用户" + (i + 1),
date: this.renderTime(),
sex: this.sex(i),
};
this.userList.push(user);
if (!i) {
this.dataTimeStart = new Date().getTime();
}
if (i == 99999) {
this.dataTimeEnd = new Date().getTime();
this.dataTemp = this.dataTimeEnd - this.dataTimeStart;
}
})(i);
}
this.cloneLIst = JSON.parse(JSON.stringify(this.userList));
this.userList = [];
this.total = this.cloneLIst.length;
},

先看下未分页直接渲染页面会有啥影响,如下:

从上面的视频可以看到直接往页面插入十万条数据并渲染,会造成页面卡顿以及白屏,页面性能极低。

渲染完成的时间也从31ms变成了40576ms

实现对数据的分页,code如下:

definePage() {
this.loading = true;
let newArr = [];
let len = this.total;
let n = this.size; // 每页多少条
let lineNum = len % 4 === 0 ? len / 4 : Math.floor(len / 4 + 1);
for (let i = 0; i < lineNum; i++) {
let temp = this.cloneLIst.slice(i * n, i * n + n);
newArr.push(temp);
}
newArr = newArr.filter((item) => {
return item.length;
});
this.userList = newArr[this.page - 1];
setTimeout(() => {
this.loading = false;
}, 500);
},

然后我们在来看下分页后的效果,如下:

很明显分页后页面流畅了好多,渲染速度也快了好多。

引入滚动插件,监听页面滚动距离,加载数据,code如下:

<!-- 引入滚动插件 -->
<div class="test-wrap-scroll">
<el-input
style="width: 240px"
placeholder="请输入性别"
v-model="keyWords"
@keyup.native.enter="query"
clearable
></el-input>
<div class="test-wrap-scroll__head">
<span>姓名</span>
<span>生日</span>
<span>性别</span>
</div>
<ul class="test-wrap-scroll__ul" v-infinite-scroll="userLoad">
<li
v-for="(item, key) in userList"
class="infinite-list-item"
:key="key"
>
<span>{{ item.name }}</span>
<span>{{ item.date }}</span>
<span>{{ item.sex }}</span>
</li>
</ul>
</div>

//自定义分页
definePage() {
this.loading = true;
let newArr = [];
let len = this.total;
let n = this.size; // 每页多少条
let lineNum = len % 4 === 0 ? len / 4 : Math.floor(len / 4 + 1);
for (let i = 0; i < lineNum; i++) {
let temp = this.cloneLIst.slice(i * n, i * n + n);
newArr.push(temp);
}
newArr = newArr.filter((item) => {
return item.length;
});
// this.userList = newArr[this.page - 1];//替换成下面插入数据方式
this.userList = [...this.userList, ...newArr[this.page - 1]];
setTimeout(() => {
this.loading = false;
this.tag = '';
}, 500);
},

//监听滚动event
userLoad() {
if(this.tag) return;
this.page++;
this.definePage();
},

效果如下:

可以看出页面非常流畅,这里我用的是饿了么自带的无限滚动组件。要是想用JavaScript监听页面滚动,可以使用window.scroll方法,判断页面滚动的距离到一定范围内加载数据,输入框查询使用防抖函数筛选重新插入数据。

防抖函数如下:

debounce(fn, time) {
return function(args) {
let that = this
clearTimeout(fn.tid)
fn.tid = setTimeout(() => {
fn.call(that, args)
}, time);
}
}

查询函数如下:

注:查询会触发滚动事件,所以这里我使用tag标记正在loading中,防止页面抖动连续插入数据。

query() {
this.page = 1;
this.createData();
this.tag = 'loading'
if (this.keyWords) {
this.cloneLIst = this.cloneLIst.filter((item) => {
let judge = ["sex"].find((i) => {
return item[i].includes(this.keyWords);
});
return judge ? item : false;
});
}
if(this.cloneLIst.length) {
this.definePage();
}
},

效果如下:

前端入门到弃坑:10000条数据怎么优化_List_02

上面的案例是在vue环境下实现的,那如果想以操作dom的方式,往页面插入dom,可以使用window自带的动画api(requestAnimationFrame),告诉浏览器接下来的操作是一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。

浏览器绘制每一帧,都会按照以下过程进行:

1、开始新的一帧率

2、处理输入事件

3、执行requestAnimationFrame

4、解析html

5、计算样式

6、更新图层树

7、发送帧

执行最后一帧的时候,可以通过cancelAnimationFrame,不要继续执行requestAnimationFrame回调了。