el-table大數據量渲染卡頓的解決方案


 

1、現象

有時候el-table的數據可能有成千上萬條,而且又要在一頁顯示完,這時候頁面渲染的dom太多了,可能會造成頁面卡頓。

解決方案:給表格固定高度,只渲染用戶在表格中滾動的視圖dom

2、卡頓原因

因為數據量過多導致瀏覽器渲染過多的標簽元素 導致DOM樹占用內存較大 使得用戶操作阻塞。
具體原理可參考別的大佬寫的文章: DOM性能瓶頸與Javascript性能優化.

3、解決方法及原理

原理
解決思路可參考: 頁面中長列表滾動的優化.

解決方法
使用第三方插件 https://www.npmjs.com/package/pl-table

<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="true">
      <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: 10000 }, (_, 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 {
        console.log(row,index);

        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 }) {
      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>

 demo: https://livelypeng.github.io/pl-table/website-project/dist/index.html 

推薦使用umy-ui


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM