Vue中 el-table大数据量加载,不分页,节省内存的性能优化
问题描述:
数据使用el-table加载,大概有1万多条。页面非常卡,查看内存占用到1.1个G,存在严重的性能问题。
考虑思路:
1、用table或者pl-table替换el-table。尝试后发现性能提升不大,仍然占用大量内存。
2、网上很多解决方案是说通过分页来加载,但我们的列表数据有关联,不能使用分页。
原因及解决方案:
经查,性能差的主要原因是,列表生成过程中产生大量的虚拟dom和真实dom,大量占用内存。
解决思路。
1、假滚动事件:拖动滚动滑块,并不会影响左侧的真实滚动,只是记录滚动的位置。
2、根据滚动的位置,计算出对应的数据区间段,比如应该取340-350这10条。
3、根据滚动位置,和数据区间,把这几条数据控制绝对定位,来模拟滚动效果。
参考的完整代码:
<template> <div class="list" style="height: 300px;overflow: scroll" ref="scrollDom" @scroll="scroll"> <div :style="{height:list.length*40+'px'}"></div> <div style="position:absolute;width: 100%" :style="{top:startIndex*40+'px'}"> <div v-for="(item,index) in splitData" :key="index" class="item"> <div v-html="item.id"></div> <div v-html="item.name"></div> <div v-html="item.createTime"></div> <div> <Button>修改</Button> <Button>删除</Button> </div> </div> </div> </div> </template> <script> export default { created() { for (let i = 0; i < 10000; i++) { this.list.push({ id: 1, name: 'name' + i, createTime: '2018-09-09' }) } }, computed: { limitCount() { return 300 / 40 + 2; }, splitData() { return this.list.slice(this.startIndex, this.startIndex + this.limitCount) } }, data() { return { startIndex: 0, list: [] } }, methods: { scroll() { // 根据滚动的距离,估算出这个滚动位置对应的数组序列,例如滚动100px,每条40px,对应第3条 let scrollTop = this.$refs.scrollDom.scrollTop; this.startIndex = Math.floor(scrollTop / 40); } } } </script> <style scoped lang="less"> .list { border: solid 1px #5f5f5f; background-color: white; margin: 100px 0; padding: 5px; width: 500px; position: relative; .item { display: flex; height: 40px; > * { flex-grow: 1; border: solid 1px #9e9e9e; padding: 3px; } } } </style>
无论怎么滚动,效果跟全部加载是一样的,但是通过右侧的控制台发现,dom数量只有固定的这几个,但是每个dom内部的值在不停的修改。
我们实际应用后的例子:
<template> <div class="qingDan" v-show="typeCur==1" v-loading="loading1"> <div class="glptLeft2"> <p @click="ChangceTableCur(1)" :class="{active:tableCur==1}">清单汇总表</p> <p @click="ChangceTableCur(2)" :class="{active:tableCur==2}">楼层明细表</p> <p @click="ChangceTableCur(3)" :class="{active:tableCur==3}">清单构件明细表</p> <p @click="ChangceTableCur(4)" :class="{active:tableCur==4}">清单构件计算书</p> <p @click="ChangceTableCur(5)" :class="{active:tableCur==5}">清单楼层计算书</p> <el-button class="daochu-btn" type="primary" @click="DowloadTable()">导出</el-button> </div> <div class="qingDanRight" style="height:100%;overflow: auto;position:relative" ref="scrollDom" @scroll="scroll"> <div :style="{height:tableData.length*40+'px'}"></div> <table style="position:absolute" :style="{top:(startIndex*40)+'px'}"> <tr class="tabletitle"> <td width="80">序号</td> <td width="100" v-if="tableCur==5">楼层</td> <td width="200">编码</td> <td width="300">项目名称</td> <td width="260">项目特征</td> <td width="100" v-if="tableCur==2">楼层</td> <td width="80">单位</td> <td width="100">工程量</td> <td width="100" v-if="tableCur==1">图形工程量</td> <td width="300" v-if="tableCur==4||tableCur==5">计算式</td> <td width="200" v-if="tableCur==1||tableCur==4||tableCur==5">增加</td> <td width="200"v-if="tableCur==1||tableCur==4||tableCur==5">扣减</td> </tr> <tr v-for="(item,index) in splitData" :key='index' > <td class="center" width="80">{{item.XuHao}}</td> <td class="center" width="100"v-if="tableCur==5">{{item.LouCeng}}</td> <td width="200">{{item.FeiYongCode}}</td> <td width="300">{{item.MC}}</td> <td width="260">{{item.TZ}}</td> <td width="100" v-if="tableCur==2">{{item.LouCeng}}</td> <td class="center" width="80">{{item.DW}}</td> <td class="center" width="100">{{item.GCL}}</td> <td width="100" v-if="tableCur==1">{{item.TuXingGCL}}</td> <td width="300" v-if="tableCur==4||tableCur==5">{{item.JSGS}}</td> <td width="200" v-if="tableCur==1||tableCur==4||tableCur==5">{{item.Add}}</td> <td width="200" v-if="tableCur==1||tableCur==4||tableCur==5">{{item.Reduce}}</td> </tr> </table> </div> </div> </template> <script> import axios from "axios"; import { AxiosPostApi } from "../../../api/common/common.js"; export default { name: "TableCommon", data() { return { tableData: [], startIndex:0, loading1: false, tableCur: 1, //工程量汇总选中表 } }, props: { typeCur: Number, checkGuid: String }, created() { this.GetTableData(1); }, computed: { limitCount() { let height= document.body.scrollHeight; return height / 40 + 2; }, splitData() { return this.tableData.slice(this.startIndex, this.startIndex + this.limitCount) } }, methods: { //导出清单表 DowloadTable() { let data = { Method: 'SLHuiZongExport', DW_EngineerID: this.checkGuid } AxiosPostApi('', data).then(response => { window.open(process.env.API_ROOT_FILE + response.data.filePath, "_blank"); }); }, //切换工程量表 ChangceTableCur(e) { this.tableCur = e; this.GetTableData() }, //获取工程量表 GetTableData(e) { this.loading1 = true; let data = { Method: 'SLHuiZong', DW_EngineerID: this.checkGuid, oType: this.tableCur } AxiosPostApi('', data).then(response => { this.tableData = response.data.LstHZView this.loading1 = false; }); }, scroll() { // 根据滚动的距离,估算出这个滚动位置对应的数组序列,例如滚动100px,每条40px,对应第3条 let scrollTop = this.$refs.scrollDom.scrollTop; this.startIndex = Math.floor(scrollTop / 40); } }, watch: { checkGuid(){ this.GetTableData(1); this.tableCur = 1; } }, } </script> <style scoped> @import "../../../assets/css/chengGuoJiaoFuDetail.css"; .el-breadcrumb__inner.is-link, .el-breadcrumb__inner span { color: #257ed8; font-weight: 700; cursor: pointer; } /* 圆环 */ .circle /deep/ .el-progress-circle { width: 50px !important; height: 50px !important; } .circle /deep/ .el-progress__text { color: #599adb; font-size: 15px !important; } </style> <style> /* 切换单位工程 */ .el-tree-node__content { height: 40px; padding-left: 20px !important; } .el-tree-node__children .el-tree-node__content { height: 40px; padding-left: 40px !important; } .el-breadcrumb__inner.is-link { color: #257ed8; } .el-drawer__body { padding: 20px 0px; } .tabletitle td,.center{ text-align: center; } table td{ height: 40px; } </style>
效果及内存:
参考文档:https://www.cnblogs.com/wanghaoran5555/articles/11177990.html