打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
Element的穿梭框数据量大时点击全选卡顿的解决方案
目录
· 方案一:复制EUI的transfer组件,然后进行修改,再引入项目目录
· 方案二:分页操作
· 分析
· 方案
现象:我们渲染了9999条数据,由于transfer组件会一次性渲染所有数据,所以一次性渲染这么多,卡个几十秒很正常好吧。所以懒加载或者分页是基本操作,方案二是分页操作。
懒加载的方式可以用EUI的无限滚动:https://element.eleme.cn/
即便我们做了懒加载之后,点击全选依旧是卡顿6秒以上,所以方案一解决的是:即便做了懒加载或者分页操作后,用户点击分页,依旧会卡顿几秒的情况。
这个是因为transfer的源码中'全选判断'代码性能差的原因,方案一就是修改transfer的源码。
我提交了一个pr,地址是: hhttps://github.com/ElemeFE/element/pull/20282
方案一:复制EUI的transfer组件,然后进行修改,再引入项目目录
EUI的transfer组件目录路径:node_modules\element-ui\packages\transfer,复制文件夹,然后放入vue项目路径的
在调用EUI的transfer的地方引入公共的组件transfer,
1
2
3
4
5
6
7
8
9
10
11
12
<template>
<Transfer v-model="value" :data="data"></Transfer>
</template>
<script>
import Transfer from '../common/transfer'
export default {
components:{
Transfer:Transfer
},
//省略
</script>
开始修改transfer代码:
打开src/common\transfer\src\transfer-panel.vue的组件,
找到updateAllChecked函数,updateAllChecked函数作用是:我们点击一个item就需要判断,看代码注释。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
updateAllChecked() {
/*
源码
this.checkableData是对象数组  我们需要的是每个对象中的key
所以checkableDataKeys保存着对象的key的数组 含义是'可通过点击进行选择的item项'的集合
*/
let start = new Date().getTime();
const checkableDataKeys = this.checkableData.map(
item => item[this.keyProp]
);
this.allChecked =
checkableDataKeys.length > 0 &&
/*
从2.4.0到现在都没改变 诶,不得不说开发团队是真的忙啊
this.checked保存着'用户通过点击item选中的item数组'
如果this.checked存在着checkableDataKeys的每一项的话,那么allChecked就是true,但凡有一项不存在就为false。allChecked代表是否全部选中了。
这里的时间复杂度是n^2,狠垃圾
*/
checkableDataKeys.every(item => this.checked.indexOf(item) > -1);
console.log("updateAllCheckedEnd", new Date().getTime() - start);
},
来看源码的耗时:
然后我们开始重写updateAllChecked函数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
updateAllChecked() {
/*
修改
这里就是高效数组中含有另一个数组的元素的算法
构建元素对象
*/
let start = new Date().getTime();
let checkableDataKeys = this.checkableData.map((item) => {
let keyProps = {};
keyProps[item[this.keyProp]] = true;
return keyProps;
});
// 通过对象的k-v对应,n(1)的方式寻找数组中是否存在某元素
this.allChecked =
checkableDataKeys.length > 0 &&
this.checked.length > 0 &&
this.checked.every((item) => checkableDataKeys[item]);
// 上面被注释的源码是最耗时的,所有一直看耗时就可以了
console.log("updateAllCheckedEnd", new Date().getTime() - start);
},
这样性能就高好多了,其实就是基本的前端算法题,目测EUI的开发者是因为懒才不写的。
来看修改代码后的耗时:
明显快多了。
接下来是文件:\src\common\transfer\src\main.vue,找到addToRight函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
addToRight() {
let currentValue = this.value.slice();
const itemsToBeMoved = [];
const key = this.props.key;
let start = new Date().getTime();
// 此处套了两层循环,耗时长
this.data.forEach((item) => {
const itemKey = item[key];
if (
this.leftChecked.indexOf(itemKey) > -1 &&
this.value.indexOf(itemKey) === -1
) {
itemsToBeMoved.push(itemKey);
}
});
console.log("addToRightEnd", new Date().getTime() - start);
currentValue =
this.targetOrder === "unshift"
? itemsToBeMoved.concat(currentValue)
: currentValue.concat(itemsToBeMoved);
this.$emit("input", currentValue);
this.$emit("change", currentValue, "right", this.leftChecked);
},
移动选中的耗时:
修改addToRight函数,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
addToRight() {
let start = new Date().getTime();
let currentValue = this.value.slice();
const itemsToBeMoved = [];
const key = this.props.key;
// 修改
let leftCheckedKeyPropsObj = {};
this.leftChecked.forEach((item, index) => {
leftCheckedKeyPropsObj[item] = true;
});
let valueKeyPropsObj = {};
this.value.forEach((item, index) => {
valueKeyPropsObj[item] = true;
});
this.data.forEach((item) => {
const itemKey = item[key];
if ( leftCheckedKeyPropsObj[itemKey] && !valueKeyPropsObj[itemKey] ) {
itemsToBeMoved.push(itemKey);
}
});
console.log("addToRightEnd", new Date().getTime() - start);
currentValue =
this.targetOrder === "unshift"
? itemsToBeMoved.concat(currentValue)
: currentValue.concat(itemsToBeMoved);
this.$emit("input", currentValue);
this.$emit("change", currentValue, "right", this.leftChecked);
},
移动选中耗时:
耗时明显减少了,这方案的前提就是懒加载或者分页,我试了一下10w的数据量,依旧是不错的。
方案二:分页操作
分析
checkBox-group有个check数组(用来记录已经选中的item数组)和renderItem数组(实际渲染的item,由于是分页,所有不会渲染所有),如果`check数组`中有`renderItem数组`的一项,那么该项就会被标记为已选,否则是未选。实现原理就是单纯的check数组和renderItem数组进行比较。
当用户点击全选的时候,check数组变成上万条数据的数组,此时我们渲染了100条数据,那么就要进行10000x100级别的循环,这就是耗时的原因所在。
其实,页面只渲染了100条数据,我们没必要将上万条数据一次性放入check数组中,我们只需要把这100条数组放入check数组,显示这100条数据为已选即可。当页面渲染了更多数据的同时,将新增的数据添加进check数组即可。这样性能大大提升。
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
JS forEach的用法
【干货】js 数组详细操作方法及解析合集
RACSignal 冷信号和热信号底层实现分析
数组
JavaScript中数组常用方法汇总(ES5)
JS - 基础学习(6): reduce() 方法
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服