el-table虚拟滚动

当表格数据量大时,渲染页面时间会很长,影响用户体验

虚拟滚动,当发生滚动时,仅从全部表格数据中截取需要展示的数据进行渲染,从而减少页面渲染时间

注:本案例适用于vue+element ui,其他情况可借鉴思路

数据

data(){
    return {
        // 全部数据,这里就不赋值啦
        data: [],
        // 表格切片数据开始索引
        startIndex: 0,
        // 虚拟滚动元素
        vScrollEle: null,
        // 实际表格元素
        rScrollEle: null,
        // 表格包裹元素
        tableWrapper: null,
        // 表格行高
        rowHeight: 40,
    }
}

computed(){
    // 表格切片数据 只截取需要渲染的部分
    slicedData(){
        return this.data.slice(this.startIndex, this.startIndex + Math.ceil(this.tableWrapper.clientHeight / rowHeight) > this.data.length ? this.data.length : this.startIndex  + Math.ceil(this.tableWrapper.clientHeight / rowHeight))
    }
}

HTML+CSS

<el-table ref="tableRef" row-key="id" :data="slicedData">
    <!--内容省略啦->
</el-table>
  • row-key 用于 diff 算法,同一行不必重复渲染,直接复用
mounted(){
    // 创建虚拟滚动的元素
    this.vScrollEle = document.createElement('div');
    // 设置虚拟滚动元素高度——全部表格数据
    this.vScrollEle.style.height = this.data.length;
    // 将虚拟滚动元素添加到表格包裹元素中,撑开它
    document.querySelector('.el-table__body-wrapper').appendChild(this.vScrollEle);
    // 获取实际表格元素.el-table__body
    this.rScrollEle = this.vScrollEle.parentNode.children[0];
    // 获取表格包裹元素.el-table__body-wrapper
    this.bodyWrapper= this.vScrollEle.parentNode;
}
::v-deep .el-table__body {
    position: absolute;
}
  • 记得将实际表格元素设置为绝对定位 

滚动事件监听

mounted(){
    // passive设置为true,可优化滚屏性能
    this.tableWrapper.addEventListner('scroll', this.scrollHandler, {passive : true});
}
methods(){
    scrollHandler(e){
        const {scrollTop, clientHeight} = e.target;
        // 更新开始索引
        this.startIndex = Math.floor(scrollTop / this.rowHeight);
        // 滚动实际表格元素
        // 判断是否滚动到顶部
        if(scrollTop > 0){
            // 不是顶部,要回滚一部分,让最下方的行完整呈现
            this.rScrollEle.style.top = `${scrollTop - (clientHeight % this.rowHeight ? 44 - clientHeight % this.rowHeight : 0)}px`;
        }else {
            this.rScrollEle.style.top = '0px';
        }
    }
}

 固定列

如果表格还设置了固定列,可能会出现以下问题:

1. 滚动时多行高亮问题

因为在设置了固定列后,经过高亮由类名 hover-row 实现,而我们又设置了表格的 row-key 属性

思考了很久,想到了个办法——滚动时删除该类名

scrollHandler(){
    for(let hoverRow of document.querySeletorAll('tr.hover-row')){
        hoverRow.classList.remove('hover-row');
    }
}

2. 固定列高亮错位问题

el-table的固定列是通过复制整个表格实现的

所以我们可以考虑对这个拷贝的表格做完全相同的虚拟滚动操作

虚拟滚动+异步加载树形数据

核心思路: 监听节点的展开和关闭,动态调整虚拟滚动元素的高度,并维护第一层级每一行之前的行数属性rowNum

// el-table的expand-change事件
expandChange(row, expanded){
    const vHeight = parseInt(this.vscrollEle.style.height);
    
    if(expanded == false){
        this.vscrollEle.style.height = `${vHeight - this.rowHeight}px`;
        this.updateRowNum(row, expanded);
    }else {
        this.$nextTick(()=>{
            // 异步加载有子节点
            if(row.hasChildren){
                this.vscrollEle.style.height = `${vHeight + this.rowHeight}px`;
                this.updateRowNum(row, expanded);
            }
        })
    }
}

// 维护第一层级每一行之前的行数
updateRowNum(row, expanded){
    let index = this.data.findIndex((item) => item.id === row.id) + 1;
    for(; index < this.data.length; index++){
        // 请求数据之后添加rowNum属性,并赋值为该行索引
        this.data[index].rowNum += expanded ? 1 : -1;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值