给elementui的table组件加上虚拟列表

·  阅读 4806

说在前头

关于什么是虚拟列表,这里不赘述。elementui的table组件本身并不支持虚拟列表功能,在对付上100条数据数十列的情况下渲染就很慢,对列表的各种操作都慢,比如点个编辑打开弹窗等等,肉眼可见的延迟。数据越多这样的卡顿越是明显。最近在做的项目就有用到elementui,80%的页面都是各种表格,有的表格要求最低行数100,最高1000,列最少15列,最多达20多列,渲染、操作各项体验非常难受。 所以,虚拟列表势在必行。

明确思路

对于超长的表格,在请求到数据之后,先取15条出来交给table组件渲染。剩下的,在滚动列表个时候监听一下表格的滚动条,在逐步添加数据,暴露在视野中的就渲染,其他直接false掉。这里有一个问题需要解决,就是初始下的15条并不能给表格带来符合100条的滚动高度,这里提供两种办法:

  • 使用一个标签,设置高度为总长度的高 单行的高 * 当前页总条数
  • 不使用标签,直接使用伪元素,给伪元素设置高 单行的高 * 当前页总条数

在改变每页条数的情况下,需要修改这个高度,考虑到js不容易修改伪元素的高度,所以采用真实标签的做法。

开搞

在进入__mounted__生命周期时,对__table组件初始化__。

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中
复制代码

同时,对表格的滚动条添加监听事件

document.querySelector('.el-table__body-wrapper').addEventListener('scroll', () => {
//xxx
})
复制代码

在拿到数据之后对数据做截断处理

$.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
        }
      }
    }
  })
复制代码

回过头,添加滚动事件的逻辑

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)
      }
    })
  })
复制代码

到这里,基本的功能初步实现了。接下来就是处理表格的多选。

表多选

在table组件上绑定__select-all、select__两个事件。

@select-all="handleSelectionAll" @select="handleSelectionChange"
复制代码

methods 中添加对应的方法和逻辑

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 = []
复制代码
分类:
前端
标签:
分类:
前端
标签:
收藏成功!
已添加到「」, 点击更改