最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 手写一个React虚拟加载表格

    正文概述 掘金(MIZY的奇幻旅程)   2020-12-28   842

    前言

    又一次使用antd-table的糟糕体验,让我不得不去考虑自己手写一个虚拟加载的表格。antd-table不支持表头的虚拟化加载,另外也不支持虚拟化加载的同时支持表头伸缩,而这些功能在大数据领域是很常见的展示需求。对于其他的虚拟加载表格,也没见到很好能同时支持虚拟加载和表头伸缩的库,于是便手写一个虚拟表格自用。

    架构准备

    首先需要明确虚拟表格的需求点,有哪些是组件需要实现的功能,哪些是用户需要实现的功能。

    • 支持纵向和横向虚拟加载
    • 支持表头虚拟加载
    • 支持表头伸缩
    • 支持自定义表内容渲染
    • 支持表头和列固定

    确定出入参

    • 行数 - columnCount
    • 列宽度数组(用来横向滚动虚拟化加载)- widths
    • renderCell函数(渲染单个单元格)- onCell
    • 表格宽高 - width,height
    • 行高度(不用数组,因为一般行是相等高度)- rowHeight
    • 列数 - rowCount

    分析逻辑

    由于需要同时支持表格的行和列的虚拟加载,我们需要同时动态计算横向和纵向在视窗内应该展示的表格内容,可以通过每个单元格距离滑动区块的左上角的距离是否在视窗边距内实现。大致逻辑如下图。 手写一个React虚拟加载表格

    按照这个图的计算逻辑,我们需要遍历 row*col次,对所有单元格计算是否在[scrollX,scrollY],[scrollX+width,scrollY+Height]的范围内。然后让在范围内的单元格执行onCell函数即可

    优化逻辑

    按照上述逻辑会存在一定的性能问题,具体体现在没必要对所有单元格进行遍历,而只需要计算出左上角的单元格的索引,和右下角的单元格索引,即计算出startRow,startCol和endRow,endCol。如下图黄色部分,我们计算出最左边出现的第一个元素的row和col,然后再记录下最后一个出现的元素的row和col,这样只需要 (endRow-startRow) * (endCol-startCol)次循环即可。同时我们可以提前缓存每一列的宽度,这样只需最多columnCount次就可以获取到边界的row和col值。

    手写一个React虚拟加载表格

    另外我们使用React进行表格渲染,可以额外使用React Key进行diff优化,即保证所有的key都与col和row有相关性,这样会保证相同的元素出现在视窗中不会重复渲染。

    编写代码

    缓存并获取表格的总宽度

    const {widths} = this.props;
    this.cacheTotalWidth = 0;
    // 缓存到每一列最右侧的总宽度
    this.cacheWidths = [];
    widths.forEach((item, index)=>{
        this.cacheTotalWidth += item;
        this.cacheWidths[index] = this.cacheTotalWidth;
    });
    

    获取边界的row和col

    const {onCell, rowCount, columnCount, width, height, rowHeight, fixHead} = this.props;
    let startColumn ; let endColumn = columnCount - 1; let startRow ;let endRow;
    const {scrollLeft, scrollTop} = this.state;
    
    // 开始计算滚动到哪个区间
    const endWidth = scrollLeft + width ;
    for (let i = 0;i < this.cacheWidths.length;i++) {
        const nowWidth = this.cacheWidths[i];
        if (startColumn === undefined && nowWidth > scrollLeft) {
            startColumn = i;
        }
        if (nowWidth >= endWidth) {
            endColumn = i;
            break;
        }
    }
    
    startRow = Math.floor(scrollTop / rowHeight);
    endRow = Math.min(Math.ceil((scrollTop + height) / rowHeight) - 1, rowCount);
    

    进行渲染

    const cells = [];
    
    for (let i = startColumn;i <= endColumn;i++) {
        for (let j = startRow;j <= endRow;j++) {
            cells.push(onCell({rowIndex: j, columnIndex: i, key: `${i}_${j}`, style: {
                position: 'absolute',
                transform: `translate(${this.cacheWidths[i - 1] || 0}px,${j * rowHeight}px)`,
                width: this.widths[i],
                height: rowHeight
            }}));
        }
    }
    return cells;
    

    搭配Resizeable

    Resizeable的核心是动态生成column的宽度数组,因此我们在onResize时,重新设置widths即可

    onResize = (index, value)=>{
    const {widths} = this.state;
      widths[index] = value.size.width;
      this.setState({
          widths: {...widths}
      });
    }
    

    最终效果

    搭配上样式 手写一个React虚拟加载表格 此时渲染一个1000*1000且支持resize的数组就很流畅了。

    Demo地址:https://mizy.github.io/react-virtual-table/dist/index.html


    起源地下载网 » 手写一个React虚拟加载表格

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元