使用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 评论

发表评论

精品游戏◆乐于分享


Title