antd Table 源码分析

简介

antd的部分组件是基于react-component封装而来的,今天我们要讲的antd table也是基于react-component/**table**封装而来的,这次antd的源码解析,大部分也是基于rc-table,这次源码分析主要是讲一些特殊的知识点。如果想了解更多,可以评论一下,我看到评论后会添加。

antd Table

先上源码的链接
从这里可以看到antd Table的代码只有500多行,主要是涉及下面几个部分

1、分页

分页这块是没有基于rc-table,在里面引用了antd的Pagination
pagination引用

在这里插入图片描述

position设置为top,表格可以在头部

2、补充了表头的筛选和查询,选中单行

源码https://github.com/ant-design/ant-design/blob/master/components/table/Table.tsx#L454

const transformColumns = React.useCallback(
    (innerColumns: ColumnsType<RecordType>): ColumnsType<RecordType> =>
      transformTitleColumns(
        transformSelectionColumns(transformFilterColumns(transformSorterColumns(innerColumns))),
      ),
    [transformSorterColumns, transformFilterColumns, transformSelectionColumns],
  );

从代码这里就能看出,筛选,排序,选中函数调用是有优先级的Sorter > Filter > Selection

rc-table

讲到rc-table我就讲点里面特殊点的功能吧

1、树状数据的展示

大家在使用antd的时候,如果传入树状的结构,table里面的树就会树状展示,那么看看是怎么实现这个功能的

这里主要讲两点

1、数据拍平

查看源码
在这里插入图片描述

源码里面使用了一个hook函数useFlattenRecords,以data, childrenColumnName, expandedKeys作为参数,然后返回拍平后的数据。
childrenColumnName是一个字符串,默认是’children’。如果用son来表示子集,就需要自己传这个参数。


//如果数据源是这样的
dataSource = [
    name:'zs',
    son:[{
        name:'z4',
        son:[{
            name:'z5'
        }]
    }]
]

<Table 
    dataSource={dataSource}
    childrenColumnName='son'
/>

useFlattenRecords函数


/**
 * flat tree data on expanded state
 *
 * @export
 * @template T
 * @param {*} data : table data
 * @param {string} childrenColumnName : 指定树形结构的列名
 * @param {Set<Key>} expandedKeys : 展开的行对应的keys
 * @param {GetRowKey<T>} getRowKey  : 获取当前rowKey的方法
 * @returns flattened data
 */
export default function useFlattenRecords<T>(
  data,
  childrenColumnName: string,
  expandedKeys: Set<Key>,
  getRowKey: GetRowKey<T>,
) {
  const arr: { record: T; indent: number; index: number }[] = React.useMemo(() => {
    if (expandedKeys?.size) {
    //当存在展开行的key时,会调用递归函数flatRecord,把所有需要展开的数据都取出来
      const temp: { record: T; indent: number; index: number }[] = [];

      // collect flattened record
      for (let i = 0; i < data?.length; i += 1) {
        const record = data[i];
    //flatRecord的结果是这棵树下要展开的子节点
        temp.push(...flatRecord<T>(record, 0, childrenColumnName, expandedKeys, getRowKey, i));
      }

      return temp;
    }

    return data?.map((item, index) => {
      return {
        record: item,
        indent: 0,
        index,
      };
    });
  }, [data, childrenColumnName, expandedKeys, getRowKey]);

  return arr;
}

2、点击展开收起

通过上面其实就可以知道展开收起就是控制expandedKeys,展开了这一行,这行的key就存到expandedKeys里面,否则就移出,然后通过监听expandedKeys的变化,重新的更新数据。

2、合并表头

合并表头源码

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ResrpVnz-1667878265579)(https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/416e8a16b4ec46c7b694c6e7c9dd9767~tplv-k3u1fbpfcp-watermark.image?)]

源码里面,会把传进来的columns转换成一个二维数组

parseHeaderRows函数主要也是在里面调用递归函数fillRowCells,把每一次的数据存到rows里面,最后得到的rows就是拍平后的二维数据

function parseHeaderRows<RecordType>(
  rootColumns: ColumnsType<RecordType>,
): CellType<RecordType>[][] {
  const rows: CellType<RecordType>[][] = [];

  function fillRowCells(
    columns: ColumnsType<RecordType>,
    colIndex: number,
    rowIndex: number = 0,
  ): number[] {
    // Init rows
    rows[rowIndex] = rows[rowIndex] || [];

    let currentColIndex = colIndex;
    
    //过滤吊空数据
    const colSpans: number[] = columns.filter(Boolean).map(column => {
      const cell: CellType<RecordType> = {
        key: column.key,
        className: column.className || '',
        children: column.title,
        column,
        colStart: currentColIndex,
      };

      let colSpan: number = 1;

        //需要合并的列
      const subColumns = (column as ColumnGroupType<RecordType>).children;
      if (subColumns && subColumns.length > 0) {
      存在需要合并的列,继续向下递归
        colSpan = fillRowCells(subColumns, currentColIndex, rowIndex + 1).reduce(
          (total, count) => total + count,
          0,
        );
        cell.hasSubColumns = true;
      }

      if ('colSpan' in column) {
        ({ colSpan } = column);
      }

      if ('rowSpan' in column) {
        cell.rowSpan = column.rowSpan;
      }

      cell.colSpan = colSpan;
      cell.colEnd = cell.colStart + colSpan - 1;
      rows[rowIndex].push(cell);

      currentColIndex += colSpan;
        //返回当成有效的列
      return colSpan;
    });

    return colSpans;
  }

  // Generate `rows` cell data
  调用递归函数,传入当前层级的数据,第几层
  fillRowCells(rootColumns, 0);

  // Handle `rowSpan`
  const rowCount = rows.length;
  for (let rowIndex = 0; rowIndex < rowCount; rowIndex += 1) {
    rows[rowIndex].forEach(cell => {
      if (!('rowSpan' in cell) && !cell.hasSubColumns) {
        // eslint-disable-next-line no-param-reassign
        cell.rowSpan = rowCount - rowIndex;
      }
    });
  }

  return rows;
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值