问题背景
最近测试丢给我一个bug:分组的编辑页面点击退出之后没有反应,一直卡着,持续了一分钟左右才退出来。
没得说,优化吧。
问题分析
这是一个分组编辑的页面,负责对设备进行分组编辑。在这个页面点击退出的时候会判断用户有没有进行编辑操作,如果有编辑操作但是没有保存,那就会拦截退出,弹窗提示用户保存。以此来避免用户误操作。
这次卡顿是因为这个分组里有7000多台设备,导致最后判断用户是否进行过编辑的计算耗时太长.看来得看看我的实现方法有什么问题了.
实现方法
我当时实现这个功能的思路是对比页面的原始数据和退出时候的数据。这是个常规思路,这个思路很简单,优点就是不用考虑中间过程,只考虑两个节点的状态就行了.
我当时用了这样的一个方法来判断这两个节点的数据(这是两个由对象组成的数组)是否一致:
function hasEdit (old_data, now_data) {
let has_edit = false;
if (old_data.length == now_data.length) {
let new_data_str = JSON.stringify(now_data);
let ret = old_data.find(item => {
let reg = new RegExp(`"id":"${item.id}"`, 'g');
return reg.test(new_data_str);
});
has_edit = !!ret;
} else {
has_edit = true;
}
return has_edit;
}
我这里还美滋滋的感觉自己用了一个JSON.stringify
,减少了循环,降低了时间复杂度。
但是实际上问题就出在这个JSON.stringify
,从这篇文章(如何提升JSON.stringify()的性能?),我们可以看出来,其实JSON.stringify
是一个很慢的操作,而且他的底层实现就是循环。这里看起来的优势并不存在。另外测试数据也证明了这一点,在用4000条数据测试时的耗时为55701ms
。这也就是上面提到的卡顿的根本原因。
那么,我们换用循环来实现会不会好点呢?
优化实现
find
循环实现
function hasEdit (old_data, now_data) {
let has_edit = false;
if (old_data.length == now_data.length) {
let ret = old_data.find(item => {
let result = now_data.find(now_item => {
return now_item.id == item.id;
})
return !!result;
});
has_edit = !!ret;
} else {
has_edit = true;
}
return has_edit;
}
双层循环,两个find
找到就停止循环,测了一下时间1483ms
,虽然性能提升了将近40倍,但是这个时间还是大,还是有卡顿的感觉,尤其是在什么操作都没有做就退出的极端情况下更明显.看来还需要优化,但是在这个思路下好像也没有什么更好的优化方法了,看来要换个思路了.
更改实现思路
在技穷之后,我换了个思路,既然对比前后的不同性能开支太大,那就记录用户操作吧.
用户勾选或者取消勾选设备的时候判断操作的设备id是否已经被记录,如果有记录过,那就删除记录;反之,就记录下.这样退出的时候判断记录是否为空来判断是否有编辑.
这个方法看起来繁琐,但是实现起来其实很简单:
function recordChangeDevices (id) {
let index = this.change_deivces.indexOf(id); // change_deivces已经声明过
if (index >= 0) {
this.change_deivces.splice(index, 1);
} else {
this.change_deivces.push(id);
}
}
这样相当于把大数据量的diff计算分摊在用户每次做操作的时候.操作了一下,发现没有卡顿发生,顺便象征性的测了下时间,0ms.nice!卡顿问题成功解决.看来这个思路还不错.
小思考
还是太无知啊,对原理不了解,就认为JSON.stringify
这样原生方法会优于我自己写的循环,还美滋滋的认为做了一个讨巧的操作.看来应该加深对原理性知识的认知.
另外,以后在做需求的时候也应该多考虑一下是否会出现大数据量,出现的大数据量的时候我的实现方法是否能扛得住,尤其是在业务的关键点,以免产出这种卡成翔的页面.真是惭愧啊.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。