使用pl-table解决 vue+elment el-table 或el-tree (表格列表或树形控件)渲染万条以上的大数据,数据过多导致卡顿问题
Table of Contents
1、优化效果
2、卡顿原因
因为数据量过多导致浏览器渲染过多的标签元素
导致DOM树占用内存较大
使得用户操作阻塞。
具体原理可参考别的大佬写的文章:
DOM性能瓶颈与Javascript性能优化.
3、解决方法及原理
原理
解决思路可参考: [页面中长列表滚动的优化](https://www.xiabingbao.com/post/scroll/longlist- optimization.html).
解决方法
使用第三方插件 https://github.com/livelyPeng/pl-
table.
具体使用方法也需参照上面github
这里el-tree
卡顿直接去上述链接github参照pl-table
进行修改并修改部分样式就是一个el-tree
组件了
下面是简略的使用方法!!!
例子:
1.安装pl-table
npm i pl-table
* 1
2.main.js文件直接引入
import plTable from 'pl-table'
import 'pl-table/themes/index.css' // 引入样式(必须引入),vuecli3.0不需要配置,cli2.0请查看webpack是否配置了url-loader对woff,ttf文件的引用,不配置会报错哦
import 'pl-table/themes/plTableStyle.css' // 默认表格样式很丑 引入这个样式就可以好看啦(如果你不喜欢这个样式,就不要引入,不引入就跟ele表格样式一样)
Vue.use(plTable);
* 1
* 2
* 3
* 4
* 5
* 6
* 7
3.pl-table
基于el-table
,所以继承了el-table
组件的所有方法
只需要把所有带el-table
开头的标签改为pl-table
开头 并加入use-virtual
属性
如:<pl-table use-virtual ></pl-table>
下面代码是github原封不动复制过来的
<template>
<!-- 使用 useVirtual 属性开启虚拟滚动 使用虚拟滚动时,必须要固定表格高度和行高 -->
<div style="height: 100%;width: 100%;padding: 0 30px">
<div style="color:red;">pl-table在线预览,更多玩法请看文档哦,欢迎Star</div>
<el-button @click="$router.push({ path: '/text' })">去子页面(为了测试缓存组件)</el-button>
<div>
<el-button @click="allSelection">全选</el-button>
<el-button @click="clearSelection">清除选中</el-button>
<el-button @click="setData(3)">变化数据为3条</el-button>
<el-button @click="setData(200)">变化数据为200条</el-button>
<el-button @click="setData(1000)">变化数据为1000条</el-button>
<el-button @click="pagingScrollTopLeft(1000)">滚动到1千位置</el-button>
<el-button @click="pagingScrollTopLeft(2000)">滚动到2千位置</el-button>
<el-button @click="pagingScrollTopLeft(0)">滚动到顶部</el-button>
<el-button @click="scrollBottom">滚动到底部位置</el-button>
<el-button @click="setHei(400)">设置高度为400</el-button>
<el-button @click="setHei(300)">设置高度为300</el-button>
</div>
<!--我是Y轴虚拟-->
<div v-if="true">
<p style="color: red">我是Y轴虚拟</p>
<pl-table ref="plTable"
:height="height"
:data="data.tableData"
selectTrClass="selectTr"
header-drag-style
:dataChangesScrollTop="false"
:summary-method="summaryMethod"
@table-body-scroll="tableBodyScroll"
fixedColumnsRoll
inverse-current-row
bigDataCheckbox
@select-all="selectAll"
show-summary
use-virtual
:row-height="rowHeight">
<template slot="empty">
没有查询到符合条件的记录
</template>
<pl-table-column type="selection" width="55" :selectable="selectable"/>
<pl-table-column type="index" width="100" fixed/>
<!--show-overflow-tooltip属性代表超出则内容部分给出提示框-->
<pl-table-column
v-for="item in columns"
:key="item.id"
:resizable="item.resizable"
:show-overflow-tooltip="item.showOverflowTooltip"
:prop="item.prop"
:label="item.label"
:fixed="item.fixed"
:width="item.width"/>
</pl-table>
</div>
<!--我是X + Y轴同时虚拟-->
<div v-if="false">
<p style="color: red">我是X + Y轴同时虚拟</p>
<plx-table-grid :data="data.tableData"
height="300"
:show-summary="true"
:summary-method="summaryMethod"
ref="plTable2">
<plx-table-column type="selection" width="55" fixed="left"/>
<plx-table-column type="index" width="100" fixed="left"/>
<plx-table-column
v-for="item in columns2"
:key="item.id"
:resizable="item.resizable"
:prop="item.prop"
:label="item.label"
:fixed="item.fixed"/>
</plx-table-grid>
</div>
<!--我是加入分页的表格-->
<div v-if="false">
<p style="color: red">我是加入分页的表格</p>
<pl-table :data="data.tableData"
big-data-checkbox
:max-height="height"
header-drag-style
fixedColumnsRoll
use-virtual
:row-height="rowHeight"
:pagination-show="true"
:total="pageForm.total"
:page-size="pageForm.pageSize"
:current-page="pageForm.currentPage"
@handlePageSize="handlePageSize"
>
<template slot="empty">
没有查询到符合条件的记录
</template>
<pl-table-column type="selection" width="55"/>
<pl-table-column type="index" width="100" fixed/>
<!--show-overflow-tooltip属性代表超出则内容部分给出提示框-->
<pl-table-column
v-for="item in columns"
:key="item.id"
:resizable="item.resizable"
:show-overflow-tooltip="item.showOverflowTooltip"
:prop="item.prop"
:label="item.label"
:fixed="item.fixed"
:width="item.width"/>
</pl-table>
</div>
<!--我是普通的el-table树形表格,这个数据多了就卡,这就是原本的el-table树表格,必须指定 row-key-->
<div v-if="false">
<p style="color: red;">我是普通的el-table树形表格,这个数据多了就卡,这就是原本的el-table树表格,必须指定 row-key</p>
<pl-table ref="plTable"
:height="height"
:data="treeData"
selectTrClass="selectTr"
row-key="id"
header-drag-style
@table-body-scroll="tableBodyScroll"
fixedColumnsRoll
inverse-current-row
@select-all="selectAll">
<template slot="empty">
没有查询到符合条件的记录
</template>
<!--show-overflow-tooltip属性代表超出则内容部分给出提示框-->
<pl-table-column
v-for="item in columns"
:key="item.id"
:resizable="item.resizable"
:show-overflow-tooltip="item.showOverflowTooltip"
:prop="item.prop"
:label="item.label"
:fixed="item.fixed"
:width="item.width"/>
</pl-table>
</div>
<!--我是pl-table大数据树形表格 必须指定 row-key 必须开启use-virtual-->
<div v-if="true">
<p style="color: red;">我是pl-table大数据树形表格 必须指定 row-key 必须开启use-virtual</p>
<el-button @click="$refs.plTreeTable.toggleTreeExpansion(treeData[0])">切换第一个</el-button>
<el-button @click="$refs.plTreeTable.setTreeExpansion(treeData[2], true)">展开第三个</el-button>
<el-button @click="$refs.plTreeTable.setAllTreeExpansion()">展开全部</el-button>
<el-button @click="$refs.plTreeTable.clearTreeExpand()">关闭所有</el-button>
<el-button @click="getTreeExpansionEvent">获取已展开</el-button>
<pl-table ref="plTreeTable"
:max-height="height"
:data="treeData"
selectTrClass="selectTr"
row-key="id"
bigDataCheckbox
:treeConfig="{children: 'children', expandAll: false}"
:use-virtual="true"
header-drag-style
@table-body-scroll="tableBodyScroll"
fixedColumnsRoll
inverse-current-row
@select-all="selectAll">
<template slot="empty">
没有查询到符合条件的记录
</template>
<!--pl-table大数据表格 你需要在列上指定某个列显示展开收起 treeNode属性-->
<pl-table-column
:treeNode="item.treeNode"
v-for="item in columns"
:key="item.id"
:resizable="item.resizable"
:show-overflow-tooltip="item.showOverflowTooltip"
:prop="item.prop"
:label="item.label"
:fixed="item.fixed"
:width="item.width"/>
</pl-table>
</div>
</div>
</template>
<script>
// 下面是关于pl-table的树形数据的介绍,希望读完下面的文字
// (最大数量500)当然你可以更多,那么只会导致你遍历时间多,页面等待时间长,(并非渲染节点时间长)
// 另外 就以下的这个层级,总数据量展开后,就是 500 + 500 x 3 + 3 x 1 = 2003 的总数据量
// 如果你 第一级是500, 第二级也是500, 第三级是10。 那么你的数据量就是 500 + 500 x 500 + 500 x 10 的总数据量,这是非常吓人的
// 所以结合自己情况去给树数据,不要瞎乱给下面的数据,树节点避免不鸟去递归,如果你的数据量很大很大,那么你会死在遍历上。
// 注意,注意,注意:并非第一级不能超过500,是想告诉你们嵌套里面子节点层级数据量不要太大。比如你可这样的: 第一级为1000, 第二级为2-5的数据量,
// 第三级为2-5的数据量...., 那么这样算下来,就是 1000 + 1000 x 5 + 5 x 5 = 6025的数据量,应该是可以的,但是记住要是太大的嵌套数据。那只会导致
// 程序卡在遍历数据上,因为程序需要慢慢去递归遍历。这是没有办法的。
// 但是传统el-table 或者el-tree他们数据量超过200 就会卡。 所以我们已经很好的优化了这一点。不过看来对于树形数据的要求,应该数据量不会太大。
// 你可以在pl-table的基础上去改改样式,就可以变相的去实现el-tree的组件了哦,你隐藏下头部,把行的高度给小一点。然后隐形边框线。是不是就是el-tree了呢???
var dataList = Array.from({ length: 500 }, (_, idx) => ({
id: idx + '_' + 1,
date: '2016-05-03',
name: 1,
ab: '欢迎使用pl-table',
address: idx,
children: Array.from({ length: 3 }, (_, idx2) => ({
id: idx + '_' + idx2 + '_' + 1,
date: '2016-05-03',
name: 1,
ab: '欢迎使用pl-table',
address: idx + '_' + idx2,
children: Array.from({ length: 1 }, (_, idx3) => ({
id: idx + '_' + idx2 + '_' + idx3 + '_' + 1,
date: '2016-05-03',
name: 1,
ab: '欢迎使用pl-table',
address: idx + '_' + idx2 + '_' + idx3
}))
}))
}));
export default {
name: 'home',
data() {
return {
rowHeight: 50,
columns: [
{prop: 'address', label: '日期', width: 120, treeNode: true, showOverflowTooltip: true},
{prop: 'address', label: '地址', width: 100, showOverflowTooltip: true},
{prop: 'address', label: '噜噜噜', width: 230, showOverflowTooltip: true},
{prop: 'address', label: '娃哈哈', width: 100, showOverflowTooltip: true},
{prop: 'address', label: '地址', width: 100, showOverflowTooltip: true},
{prop: 'address', label: '娃哈哈', width: 100, showOverflowTooltip: true},
{prop: 'address', label: '娃哈哈', width: 100, showOverflowTooltip: true},
{prop: 'address', label: '地址', width: 100, showOverflowTooltip: true},
{prop: 'address', label: '娃哈哈', width: 100, showOverflowTooltip: true},
{prop: 'address', label: '娃哈哈', width: 100, showOverflowTooltip: true},
{prop: 'address', label: '噜噜噜', showOverflowTooltip: true},
{prop: 'address', label: '娃哈哈', width: 100, showOverflowTooltip: true, fixed: 'right'}
],
columns2: Array.from({ length: 20 }, (_, idx) => ({
prop: 'address', label: '地址' + idx, width: 200, showOverflow: true, sortable: true, fixed: ''
})),
data: {
tableData:Array.from({ length: 20 }, (_, idx) => ({
id: idx + 1,
date: '2016-05-03',
name: 1,
ab: '欢迎使用pl-table',
address: 1 + idx
}))
},
top: 0,
height: 500,
pageForm: {
total: 1000,
pageSize: 10,
currentPage: 1
},
treeData: dataList
}
},
methods: {
selectAll (val) {
console.log(val)
},
selectable (row, index) {
if (index === 1) {
return false
} else {
return true
}
},
// 合计
summaryMethod ({ columns, data }) {
// 平均值算法(不需要自己去掉)
function cacl(arr, callback) {
let ret;
for (let i=0; i<arr.length;i++) {
ret = callback(arr[i], ret);
}
return ret;
}
// 平均值算法(不需要自己去掉)
Array.prototype.sum = function () {
return cacl(this, function (item, sum) {
if (typeof (sum) == 'undefined') {
return item;
}
else {
return sum += item;
}
});
};
// 平均值算法(不需要自己去掉)
Array.prototype.avg = function () {
if (this.length == 0) {
return 0;
}
return this.sum(this) / this.length;
};
const means = [] // 合计
const fenceSums = [] // 平均值
columns.forEach((column, columnIndex) => {
if (columnIndex === 0) {
means.push('合计')
fenceSums.push('平均值')
} else {
const values = data.map(item => Number(item[column.property]));
// 合计
if (!values.every(value => isNaN(value))) {
means[columnIndex] = values.reduce((prev, curr) => {
const value = Number(curr);
if (!isNaN(value)) {
return prev + curr;
} else {
return prev;
}
}, 0);
// means[columnIndex] += ' 元'
// 改变了ele的合计方式,扩展了合计场景
// 你以为就只有上面这样玩吗?错啦,你还可以自定义样式哦
// means[columnIndex] = '<span style="color: red">' + means[columnIndex] + '元</span>'
means[columnIndex] = '<span style="color: red">' + means[columnIndex] + '元</span><br/><span>123</span>'
} else {
means[columnIndex] = '';
}
// 平均值
const precisions = [];
let notNumber = true;
values.forEach(value => {
if (!isNaN(value)) {
notNumber = false;
let decimal = ('' + value).split('.')[1];
precisions.push(decimal ? decimal.length : 0);
}
});
if (!notNumber) {
fenceSums[columnIndex] = values.avg()
} else {
fenceSums[columnIndex] = '';
}
}
})
// 返回一个二维数组的表尾合计
return [means, fenceSums]
},
setHei (val) {
this.height = val
},
tableBodyScroll ({ scrollTop }, e) {
this.top = scrollTop
},
allSelection () {
this.$refs.plTable.toggleAllSelection()
},
clearSelection () {
this.$refs.plTable.clearSelection()
this.$refs.plTable2.clearSelection()
},
setData (num) {
this.data.tableData = Array.from({ length: num }, (_, idx) => ({
id: idx + 1,
date: '2016-05-03',
name: 1,
ab: '欢迎使用pl-table',
address: 1 + idx
}))
},
scrollBottom () {
this.$refs.plTable.scrollBottom()
},
pagingScrollTopLeft (val) {
this.$refs.plTable.pagingScrollTopLeft(val, 0)
},
// 分页事件
handlePageSize ({page, size}) {
console.log(page, size)
},
// 获取已经展开的节点
getTreeExpansionEvent () {
console.log(this.$refs.plTreeTable.getTreeExpandRecords())
}
}
}
</script>
<style>
body, html {
margin: 0;
box-sizing: border-box;
width: 100%;
height: 100%;
}
body ::-webkit-scrollbar-thumb {
-webkit-border-radius: 5px;
border-radius: 5px;
background-color: rgba(144, 147, 153, 0.5);
}
.selectTr td {
background: #ccc !important;
color: red !important;
}
</style>
* 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
* 32
* 33
* 34
* 35
* 36
* 37
* 38
* 39
* 40
* 41
* 42
* 43
* 44
* 45
* 46
* 47
* 48
* 49
* 50
* 51
* 52
* 53
* 54
* 55
* 56
* 57
* 58
* 59
* 60
* 61
* 62
* 63
* 64
* 65
* 66
* 67
* 68
* 69
* 70
* 71
* 72
* 73
* 74
* 75
* 76
* 77
* 78
* 79
* 80
* 81
* 82
* 83
* 84
* 85
* 86
* 87
* 88
* 89
* 90
* 91
* 92
* 93
* 94
* 95
* 96
* 97
* 98
* 99
* 100
* 101
* 102
* 103
* 104
* 105
* 106
* 107
* 108
* 109
* 110
* 111
* 112
* 113
* 114
* 115
* 116
* 117
* 118
* 119
* 120
* 121
* 122
* 123
* 124
* 125
* 126
* 127
* 128
* 129
* 130
* 131
* 132
* 133
* 134
* 135
* 136
* 137
* 138
* 139
* 140
* 141
* 142
* 143
* 144
* 145
* 146
* 147
* 148
* 149
* 150
* 151
* 152
* 153
* 154
* 155
* 156
* 157
* 158
* 159
* 160
* 161
* 162
* 163
* 164
* 165
* 166
* 167
* 168
* 169
* 170
* 171
* 172
* 173
* 174
* 175
* 176
* 177
* 178
* 179
* 180
* 181
* 182
* 183
* 184
* 185
* 186
* 187
* 188
* 189
* 190
* 191
* 192
* 193
* 194
* 195
* 196
* 197
* 198
* 199
* 200
* 201
* 202
* 203
* 204
* 205
* 206
* 207
* 208
* 209
* 210
* 211
* 212
* 213
* 214
* 215
* 216
* 217
* 218
* 219
* 220
* 221
* 222
* 223
* 224
* 225
* 226
* 227
* 228
* 229
* 230
* 231
* 232
* 233
* 234
* 235
* 236
* 237
* 238
* 239
* 240
* 241
* 242
* 243
* 244
* 245
* 246
* 247
* 248
* 249
* 250
* 251
* 252
* 253
* 254
* 255
* 256
* 257
* 258
* 259
* 260
* 261
* 262
* 263
* 264
* 265
* 266
* 267
* 268
* 269
* 270
* 271
* 272
* 273
* 274
* 275
* 276
* 277
* 278
* 279
* 280
* 281
* 282
* 283
* 284
* 285
* 286
* 287
* 288
* 289
* 290
* 291
* 292
* 293
* 294
* 295
* 296
* 297
* 298
* 299
* 300
* 301
* 302
* 303
* 304
* 305
* 306
* 307
* 308
* 309
* 310
* 311
* 312
* 313
* 314
* 315
* 316
* 317
* 318
* 319
* 320
* 321
* 322
* 323
* 324
* 325
* 326
* 327
* 328
* 329
* 330
* 331
* 332
* 333
* 334
* 335
* 336
* 337
* 338
* 339
* 340
* 341
* 342
* 343
* 344
* 345
* 346
* 347
* 348
* 349
* 350
* 351
* 352
* 353
* 354
* 355
* 356
* 357
* 358
* 359
* 360
* 361
* 362
* 363
* 364
* 365
* 366
* 367
* 368
* 369
* 370
* 371
* 372
* 373
* 374
* 375
* 376
* 377
* 378
* 379
* 380
* 381
* 382
* 383
* 384
* 385
* 386
* 387
* 388
* 389
* 390
* 391
* 392
* 393
* 394
demo 百度云压缩文件
链接:https://pan.baidu.com/s/1JIadGvfYd2ry6YJK5bm5Fg
提取码:os5v
复制这段内容后打开百度网盘手机App,操作更方便哦
0 评论