給elementui的table組件加上虛擬列表

說在前頭

關於什麼是虛擬列表,這裏不贅述。elementui的table組件自己並不支持虛擬列表功能,在對付上100條數據數十列的狀況下渲染就很慢,對列表的各類操做都慢,好比點個編輯打開彈窗等等,肉眼可見的延遲。數據越多這樣的卡頓越是明顯。最近在作的項目就有用到elementui,80%的頁面都是各類表格,有的表格要求最低行數100,最高1000,列最少15列,最多達20多列,渲染、操做各項體驗很是難受。 因此,虛擬列表勢在必行。css

明確思路

對於超長的表格,在請求到數據以後,先取15條出來交給table組件渲染。剩下的,在滾動列表個時候監聽一下表格的滾動條,在逐步添加數據,暴露在視野中的就渲染,其餘直接false掉。這裏有一個問題須要解決,就是初始下的15條並不能給表格帶來符合100條的滾動高度,這裏提供兩種辦法:ajax

  • 使用一個標籤,設置高度爲總長度的高 單行的高 * 當前頁總條數
  • 不使用標籤,直接使用僞元素,給僞元素設置高 單行的高 * 當前頁總條數

在改變每頁條數的狀況下,須要修改這個高度,考慮到js不容易修改僞元素的高度,因此採用真實標籤的作法。json

開搞

在進入__mounted__生命週期時,對__table組件初始化__。markdown

let i = document.createElement('i')
i.id = 'vheight'
i.style.width = '0'
i.style.float = 'right'
document.querySelector('.el-table__body-wrapper').append(i)
document.querySelector('.el-table__body').style.float = 'left'
this.tableData2 = {} //tableData2不須要在頁面顯示,故不放入data中
複製代碼

同時,對錶格的滾動條添加監聽事件app

document.querySelector('.el-table__body-wrapper').addEventListener('scroll', () => {
//xxx
})
複製代碼

在拿到數據以後對數據作截斷處理ui

$.ajax({
    url: url,
    type: 'get',
    data: {},
    dataType: 'json',
    success: (data) => {
      if (data.code === 1) {
        this.tableData2 = data.data  // 將這個總的數據存起來
        // 拿到數據以後給i加上高度撐出滾動條,42是每行的高度
        document.querySelector('#vheight').style.height = this.tableData2.leadsList.length * 42 + 'px'
        // 截斷的前15條數據放入tableData,交給table組件渲染
        this.tableData = {
          leadsList: this.tableData2.leadsList.filter((x,i) => i >= 0 && i < (15)),
          count: this.tableData2.count
        }
      }
    }
  })
複製代碼

回過頭,添加滾動事件的邏輯this

document.querySelector('.el-table__body-wrapper').addEventListener('scroll', () => {
    let s = document.querySelector('.el-table__body-wrapper').scrollTop, h = 42, c = ''
    c = Math.floor(s / h)
    // 16爲橫向滾動條的高度
    if (s >= (this.tableData2.leadsList.length * 42) - document.querySelector('.el-table__body-wrapper').offsetHeight - 16) { 
      s = (this.tableData2.leadsList.length * 42) - document.querySelector('.el-table__body-wrapper').offsetHeight
    }
    if (s <= 0) { s = 0 }
    document.querySelector('.el-table__body-wrapper .el-table__body').style.transform = `translateY(${s}px)`
    this.tableData = {
      leadsList: this.tableData2.leadsList.filter((x,i) => i >= c && i < (c + 15)),
      count: this.tableData2.count
    }
    this.$nextTick(() => {
      if (this.selectIdx.length > 0) {
        let s = this.tableData.leadsList.map(x => {
          if (this.selectIdx.includes(x.leads_id)) {
            this.$refs.mulTable.toggleRowSelection(x)
          }
          return x
        })
      }
      if (this.selectIdx.length > 0 && this.selectIdx.length < this.pageSize) {
        setTimeout(() => {
          let pro = document.querySelector('.el-table-column--selection')
          pro.querySelector('.el-checkbox__input').classList.add('is-indeterminate')
          pro.querySelector('.el-checkbox__input').classList.remove('is-checked')
        }, 100)
      }
    })
  })
複製代碼

到這裏,基本的功能初步實現了。接下來就是處理表格的多選。url

表多選

在table組件上綁定__select-all、select__兩個事件。spa

@select-all="handleSelectionAll" @select="handleSelectionChange"
複製代碼

methods 中添加對應的方法和邏輯code

methods: {
  // selectIdx存放被勾選的行
  handleSelectionChange (val, v) {
    let i = this.selectIdx.indexOf(v.leads_id)
    if (i !== -1) {
      this.selectIdx.splice(i , 1)
    } else {
      this.selectIdx.push(v.leads_id)
    }
  },
  // 表頭全選
  handleSelectionAll (val) {
  	// 有值表示全選,反之亦然
    if (val.length > 0) {
      this.selectIdx = this.tableData2.leadsList.map(x => x.leads_id)
    } else {
      this.selectIdx = []
    }
  },
}
複製代碼

在滾動監聽事件中處理滾動勾選

this.$nextTick(() => {
  if (this.selectIdx.length > 0) {
    let s = this.tableData.leadsList.map(x => {
      if (this.selectIdx.includes(x.leads_id)) {
        this.$refs.mulTable.toggleRowSelection(x)
      }
      return x
    })
  }
  // 半選下強制修改狀態
  if (this.selectIdx.length > 0 && this.selectIdx.length < this.pageSize) {
    setTimeout(() => {
      let pro = document.querySelector('.el-table-column--selection')
      pro.querySelector('.el-checkbox__input').classList.add('is-indeterminate')
      pro.querySelector('.el-checkbox__input').classList.remove('is-checked')
    }, 100)
  }
})
複製代碼

使用css實現table的fixed 功能

table組件的__fixed屬性__會固定列,可是一個fixed屬性它就複製一份表格,影響渲染速度。因此,能夠使用css的__sticky屬性__來代替。

.fixed-table {
   td:first-child, th:first-child {
    position: sticky;
    left: 0;
    z-index: 1;
    background: #fff;
  }
  td:last-child {
    position: sticky;
    right: 0;
    z-index: 1;
    background: #fff;
    border-left: 1px solid #ebeef5;
  }
  th:nth-last-child(2) {
    position: sticky;
    right: 17px;
    z-index: 1;
    border-left: 1px solid #d1d2d6;
  }
  .gutter {
    width: 17px;
    position: sticky;
    right: 0;
    z-index: 1;
  }
}
複製代碼

一些小問題的解決

  • 改變每頁顯示條數和翻頁後移除選中項
handleSizeChange: function (val) {
  this.currentPage = 1
  this.pageSize = val
  this.selectIdx = []
},
handleCurrentChange: function (val) {
  this.selectIdx = []
  this.currentPage = val
},
複製代碼
  • 從新發起請求後滾動回頂部並清除選擇項
document.querySelector('.el-table__body-wrapper').scrollTop = 0
this.selectIdx = []
複製代碼
相關文章
相關標籤/搜索