关于处理万级数据编辑表格的踩坑日志
1、简介:项目需求要弄一个数据录入表格,数据比较多并且是可编辑,按键切换输入框,左侧固定列等等,一开始表格是直接用的element,用着用着已经不满足需求,编辑数据多的时候慢的不行编辑还卡顿。
2、解决经历:一开始是打算用patch-package修改element源码的,然后发现引入的element引用的是打包后的lib,要改的话需要下载源码修改还要打包设置一个文件并修改引用路径,不然别人拉代码并不会拉到你改过的element,十分麻烦,后来找到了用Vue自定义指令不修改依赖来给element表格打补丁,试过之后发现不行还是很卡顿,之后又找到了umyui表格,说是可以处理万级数据,试了一下感觉还不错,算是不错的解决方案,于是记录一下
2-1、Vue自定义指令修改element渲染的方式(思路是表格只渲染固定的dom,滚动滚动条的时候切换dom的内容)
来源 https://segmentfault.com/q/1010000017202682
作者 Hally
新建一个loadmore.js文件
// 设置默认溢出显示数量 var spillDataNum = 20; // 设置隐藏函数 var timeout = false; let setRowDisableNone = function (topNum, showRowNum, binding) { if (timeout) { clearTimeout(timeout); } timeout = setTimeout(() => { binding.value.call(null, topNum, topNum + showRowNum + spillDataNum); }); }; export default { name: 'loadmore', componentUpdated: function (el, binding, vnode, oldVnode) { setTimeout(() => { const dataSize = vnode.data.attrs['data-size']; const oldDataSize = oldVnode.data.attrs['data-size']; if(dataSize === oldDataSize){ return; } const selectWrap = el.querySelector('.el-table__body-wrapper'); const selectTbody = selectWrap.querySelector('table tbody'); const selectRow = selectWrap.querySelector('table tr'); if (!selectRow) { return; } const rowHeight = selectRow.clientHeight; let showRowNum = Math.round(selectWrap.clientHeight / rowHeight); const createElementTR = document.createElement('tr'); let createElementTRHeight = (dataSize - showRowNum - spillDataNum) * rowHeight; createElementTR.setAttribute('style', `height: ${createElementTRHeight}px;`); selectTbody.append(createElementTR); // 监听滚动后事件 selectWrap.addEventListener('scroll', function () { let topPx = this.scrollTop - spillDataNum * rowHeight; let topNum = Math.round(topPx / rowHeight); let minTopNum = dataSize - spillDataNum - showRowNum; if (topNum > minTopNum) { topNum = minTopNum; } if (topNum < 0) { topNum = 0; topPx = 0; } selectTbody.setAttribute('style', `transform: translateY(${topPx}px)`); createElementTR.setAttribute('style', `height: ${createElementTRHeight-topPx > 0 ? createElementTRHeight-topPx : 0}px;`); setRowDisableNone(topNum, showRowNum, binding); }) }); } };
在main.js里面引入
import loadmore from '@/js/loadmore'
Vue.directive(loadmore.name,loadmore.componentUpdated);
然后在页面使用
<template> <div> <el-table border :data="filteredData" style="width: 100%" height="300" :data-size="tableData.length" v-loadmore="handelLoadmore"> <el-table-column type="selection" width="55"> </el-table-column> <el-table-column label="日期" width="180"> <template slot-scope="scope"> <div> <i class="el-icon-time"></i> <span style="margin-left: 10px">{{ scope.row.date }}</span> </div> </template> </el-table-column> <el-table-column label="日期" width="180"> <template slot-scope="scope"> <div> <i class="el-icon-time"></i> <span style="margin-left: 10px">{{ scope.row.date }}</span> </div> </template> </el-table-column> <el-table-column label="日期" width="180"> <template slot-scope="scope"> <div> <i class="el-icon-time"></i> <span style="margin-left: 10px">{{ scope.row.date }}</span> </div> </template> </el-table-column> <el-table-column label="日期" width="180"> <template slot-scope="scope"> <div> <i class="el-icon-time"></i> <span style="margin-left: 10px">{{ scope.row.date }}</span> </div> </template> </el-table-column> <el-table-column label="姓名" width="180"> <template slot-scope="scope"> <div> <el-popover trigger="hover" placement="top"> <p>姓名: {{ scope.row.name }}</p> <p>住址: {{ scope.row.address }}</p> <div slot="reference" class="name-wrapper"> <el-tag size="medium">{{ scope.row.name }}</el-tag> </div> </el-popover> </div> </template> </el-table-column> <el-table-column label="操作"> <template slot-scope="scope"> <div> <el-button size="mini" @click="handleEdit(scope.$index, scope.row)">编辑</el-button> <el-button size="mini" type="danger" @click="handleDelete(scope.$index, scope.row)">删除</el-button> </div> </template> </el-table-column> </el-table> </div> </template> <script> export default { name: 'test', components: {}, data () { return { tableData: [], currentStartIndex: 0, currentEndIndex: 20 }; }, created () { this.getTableData(); }, computed: { filteredData () { return this.tableData.filter((item, index) => { if (index < this.currentStartIndex) { return false; } else if (index > this.currentEndIndex) { return false; } else { return true; } }); } }, methods: { handelLoadmore (currentStartIndex, currentEndIndex) { this.currentStartIndex = currentStartIndex; this.currentEndIndex = currentEndIndex; }, getTableData () { let cont = 0; let tableData = []; while (cont < 30000) { cont = cont + 1; let object = { date: cont, name: '王小虎' + cont, address: '上海市普陀区金沙江路 cont 弄' } tableData.push(object); } setTimeout(() => { this.tableData = tableData; }, 2000); } }, watch: {} } </script> <style scoped> .el-table__body-wrapper .el-table__row td { display: none; } .el-table__body-wrapper .el-table__row { height: 38px; } </style>
2-2、umy-ui表格(有很详细的文档)
官网:http://www.umyui.com/
github:https://github.com/u-leo/umy-ui
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」