element-ui的穿梭框数据量大时点击“全选”卡顿
遇到问题:
数据量小的时候问题还没有暴露,当数据大概有几千条时,点击全选时页面需要大概需要三四秒,单选、移动操作也是如此,对于用户来说体验感很差。
解决办法:
可以将node_modules\element-ui\packages\transfer组件拎出来作为组件,针对组件的方法进行改写,再进行引用。
- 修改transfer-panel.vue文件里的updateAllChecked() 全选方法:
updateAllChecked() {
/*
this.checkableData是对象数组,需要每个对象里的key
checkableDataKeys是'所有可以进行选择的item'的key的数组
*/
// const checkableDataKeys = this.checkableData.map(
// (item) => item[this.keyProp]
// );
/*
this.checked是'已经选中的item'的数组
如果this.checked存在着checkableDataKeys的每一项的话,那么every方法返回true
allChecked为是否全选,这里是O(n^2)的时间复杂度
*/
// this.allChecked =
// checkableDataKeys.length > 0 &&
// checkableDataKeys.every((item) => this.checked.indexOf(item) > -1);
// 修改
let checkObj = {};
/* 含义:如果this.checked=['a','b','c'],那么checkObj={a:true,b:true,c:true} */
this.checked.forEach((item) => {
checkObj[item] = true;
});
/*
含义:item[this.keyProp]为item对象中this.keyProp(属性)的属性值,
然后使用这个值作为checkObj的属性名来查找。遍历this.checkableData数组,
如果checkObj对象的所有对应属性都存在(即checkObj[item[this.keyProp]]为true),
那么every方法返回true
*/
this.allChecked =
this.checkableData.length > 0 &&
this.checked.length > 0 &&
this.checkableData.every((item) => checkObj[item[this.keyProp]]);
},
以及单选逻辑:
watch: {
checked(val, oldVal) {
this.updateAllChecked();
// 源码
// if (this.checkChangeByUser) {
// O(n^2)的时间复杂度
// const movedKeys = val.concat(oldVal).
// filter(v => val.indexOf(v) === -1 || oldVal.indexOf(v) === -1);
// this.$emit('checked-change', val, movedKeys);
// } else {
// this.$emit('checked-change', val);
// this.checkChangeByUser = true;
// }
// 修改
let newObj = {};
val.every((item) => {
newObj[item] = true;
});
let oldObj = {};
oldVal.every((item) => {
oldObj[item] = true;
});
if (this.checkChangeByUser) {
// O(n)
const movedKeys = val
.concat(oldVal)
.filter((v) => newObj[v] || oldVal[v]);
this.$emit("checked-change", val, movedKeys);
} else {
this.$emit("checked-change", val);
this.checkChangeByUser = true;
}
},
- 修改main.vue里的addToRight() 移动方法
addToRight() {
let currentValue = this.value.slice();
const itemsToBeMoved = [];
const key = this.props.key;
// 源码
// this.data.forEach(item => {
// const itemKey = item[key];
// O(n^2)
// if (
// this.leftChecked.indexOf(itemKey) > -1 && this.value.indexOf(itemKey) === -1
// ) {
// itemsToBeMoved.push(itemKey);
// }
// });
// 修改
let leftCheckedKeyPropsObj = {};
this.leftChecked.forEach((item) => {
leftCheckedKeyPropsObj[item] = true;
});
let valueKeyPropsObj = {};
this.value.forEach((item) => {
valueKeyPropsObj[item] = true;
});
this.data.forEach((item) => {
const itemKey = item[key];
// O(n)
if ( leftCheckedKeyPropsObj[itemKey] && !valueKeyPropsObj[itemKey]) {
itemsToBeMoved.push(itemKey);
}
});
currentValue = this.targetOrder === 'unshift'
? itemsToBeMoved.concat(currentValue)
: currentValue.concat(itemsToBeMoved);
this.$emit('input', currentValue);
this.$emit('change', currentValue, 'right', this.leftChecked);
}
除此之外,还要优化两个computed:
computed: {
sourceData() {
let valueObj = {};
this.value.forEach((item)=>{
valueObj[item] = true;
});
return this.data.filter(
(item) => !valueObj[item[this.props.key]]
);
},
targetData() {
if (this.targetOrder === 'original') {
let valueObj = {};
this.value.forEach((item)=>{
valueObj[item] = true;
});
let data = this.data.filter(
(item) => valueObj[item[this.props.key]]
);
return data;
} else {
return this.value.reduce((arr, cur) => {
const val = this.dataObj[cur];
if (val) {
arr.push(val);
}
return arr;
}, []);
}
}
}