当前位置: 首页 > news >正文

Element-UI的transfer穿梭框组件数据量大解决方案

 一、面临问题

数据量大,渲染慢,搜索、勾选、关闭、右移卡顿

二、解决方案

1. 总体思路

改写 Element-UI 的 transfer 穿梭框组件,形成自己的自定义组件

2. 具体步骤

2.1 复制 Element-UI 的 transfer 穿梭框组件出来

a. 对 node_modules/element-ui 中文件结构的理解

  • 注册进vue中的element-ui组件使用的是打包后的文件夹:lib
  • packages、src、types是源码,仅供开发者查阅。
  • 我们主要需要使用 packages 里的源码

image

b. 复制出 packages 里的 transfer 组件

image

  • transfer-pannel.vue就是穿梭框的左右板子,main.vue是中控系统,index.js 是组件导出

2.2 编辑transfer-panel.vue文件,优化全选和单选

a. 优化全选卡顿问题

  • 原来代码
    updateAllChecked() {const checkableDataKeys = this.checkableData.map(item => item[this.keyProp]);// 这里是O(n^2)的时间复杂度this.allChecked = checkableDataKeys.length > 0 &&checkableDataKeys.every(item => this.checked.indexOf(item) > -1);
    },
  • 修改后代码
    updateAllChecked() {let checkObj = {};this.checked.forEach((item) => {checkObj[item] = true;});this.allChecked =this.checkableData.length > 0 &&this.checked.length > 0 &&this.checkableData.every((item) => checkObj[item[this.keyProp]]);
    },

b. 优化单选卡顿问题

  • 源来代码
    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;}},
    }
  • 修改后代码
    watch: {checked(val, oldVal) {this.updateAllChecked();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;}},
    }  

2.3 修改main.vue文件,优化移动卡顿

  • 原来代码
    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);}});currentValue = this.targetOrder === 'unshift'? itemsToBeMoved.concat(currentValue): currentValue.concat(itemsToBeMoved);this.$emit('input', currentValue);this.$emit('change', currentValue, 'right', this.leftChecked);
    },
  • 修改后代码
    addToRight() {let currentValue = this.value.slice();const itemsToBeMoved = [];const key = this.props.key;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;}, []);}}
    },

2.4 使用虚拟滚动解决渲染问题

  •  npm i vue-virtual-scroll-list
  • 创建transfer-checkbox-item.vue组件

image

内容如下:

<template><el-checkboxclass="el-transfer-panel__item":label="source[keyProp]":disabled="source[disabledProp]"><option-content :option="source"></option-content>
</el-checkbox>
</template><script>import ElCheckbox from 'element-ui/packages/checkbox';export default {name: 'transfer-checkbox-item',props: {index: { // index of current itemtype: Number},source: { // here is: {uid: 'unique_1', text: 'abc'}type: Object,default() {return {};}},keyProp: {type: String},disabledProp: {type: String}},components: {ElCheckbox,OptionContent: {props: {option: Object},render(h) {const getParent = vm => {if (vm.$options.componentName === 'ElTransferPanel') {return vm;} else if (vm.$parent) {return getParent(vm.$parent);} else {return vm;}};const panel = getParent(this);const transfer = panel.$parent || panel;return panel.renderContent? panel.renderContent(h, this.option): transfer.$scopedSlots.default? transfer.$scopedSlots.default({ option: this.option }): <span>{ this.option[panel.labelProp] || this.option[panel.keyProp] }</span>;}}}};
</script>

 

  • transfer-panel.vue做修改 引入两个东西
import Item from './transfer-checkbox-item.vue';
import VirtualList from 'vue-virtual-scroll-list';// 注册VirtualList
components: {'virtual-list': VirtualList
}
  • 初始化定义两个变量
data() {return {itemComponent: Item,virtualListProps: {}}
}
  • 定义一个computed->virtualScroll
computed: {virtualScroll() {return this.$parent.virtualScroll;},
}
  • 修改一个computed->keyProp
computed: {keyProp() {this.virtualListProps.keyProp = this.props.key || 'key';return this.props.key || 'key';}
}
  • 修改一个computed->disabledProp
computed: {disabledProp() {this.virtualListProps.disabledProp = this.props.disabled || 'disabled';return this.props.disabled || 'disabled';}
}
  • 原checkbox集合的渲染方式如下
<el-checkbox-groupv-model="checked"v-show="!hasNoMatch && data.length > 0":class="{ 'is-filterable': filterable }"class="el-transfer-panel__list"<el-checkboxclass="el-transfer-panel__item":label="item[keyProp]":disabled="item[disabledProp]":key="item[keyProp]"v-for="item in filteredData"><option-content :option="item"></option-content></el-checkbox>
</el-checkbox-group>
  • 修改为:
 <el-checkbox-groupv-model="checked"v-show="!hasNoMatch && data.length > 0":class="{ 'is-filterable': filterable }"class="el-transfer-panel__list"><virtual-list v-if="virtualScroll"style="height:100%;overflow-y: auto;":data-key="keyProp":data-sources="filteredData":data-component="itemComponent":extra-props="virtualListProps"/><template v-else><el-checkboxclass="el-transfer-panel__item":label="item[keyProp]":disabled="item[disabledProp]":key="item[keyProp]"v-for="item in filteredData"><option-content :option="item"></option-content></el-checkbox></template>
</el-checkbox-group>
  • main.vue中接受一个prop->virtualScroll
 
props:{virtualScroll: {type: Boolean,default: false}
}

 

  • 在业务代码中使用这个transfer时,得传入:virtual-scroll:true,代表开启虚拟列表功能
 
<newTransfer v-model="value" :data="data" :virtual-scroll="true"></newTransfer>

 

至此,应该是没问题的了,渲染十万条都是没问题的。

 
http://www.hskmm.com/?act=detail&tid=20955

相关文章:

  • 第9章 day09 hook插件
  • nginx 一致性hash和流量检查模块
  • 深入解析:10月底实习准备-Mysql(按面试频率准备)
  • 机器学习概述 - -一叶知秋
  • CEXE的%你赛5-题解
  • C++语言(1)
  • Windows多人共享文件夹全流程,附2025新共享文件快90%
  • 第11章 day11-day12关于json请求体/逆向爬虫实战
  • 容斥与二项式反演
  • react useCallback Hook详解
  • 从Docker构建失败到CRA被淘汰:一个React项目的ES模块探索记录
  • 充气泵PCBA方案中数字传感器和模拟传感器的差异
  • 实用指南:小米17手机的上市公司供应商
  • CDN + WAF + CLB + Higress 架构下的 TLS 加解密详细解析(适用阿里云)
  • react useMemo Hook详解
  • react useContext 详解
  • Python技能大赛-备赛建议
  • 【软件系统架构】系列七:系统性能——操作系统性能深入解析 - 实践
  • 你的下一款定位神器,何必是GPS?Nordic带你解锁Wi-Fi SSID的隐藏潜能
  • CF407E k-d-sequence 题目分析(0929模拟赛最后一题)
  • Linux 生成随机端口
  • MATLAB 中 dsp.FFT 系统对象:从原理到实践的完整指南
  • 并发编程可见性
  • C# Devexpress GridControl实现全选功能(转载,记录)
  • github Connection reset by 20.205.243.160 port 443 fatal: Could not read from remote repository.
  • VsCode Ai插件
  • 完整教程:基于完全分布式模式部署Hadoop(喂饭教程)
  • Vue 3.6 引入 Vapor Mode,虚拟DOM已死?
  • part 10
  • Nordic发布用于nRF54L系列的nRF Connect SDK裸机选项