目录
实现关键
- 为列头添加可拖拽的交互
- 拖拽时监听鼠标事件移动偏移值
- 计算得到被操作列具体宽度
- 最后赋值 columns 给 antd Table
设计思路
实现一个高阶组件,使用时将外部业务 Table 组件传入即可:
typescript
export const CommonTable = withResizableColumns((data, columns, ...rest) => return <Table columns={columns} dataSource={data} {...res} />)
具体代码
typescript
import React, { useState, useEffect } from "react"
import type { ResizeCallbackData } from "react-resizable"
import { Resizable } from "react-resizable"
import styles from "./index.module.less"
interface WithResizableColumnsProps {
columns: any[]
}
const ResizableTitle = (
props: React.HTMLAttributes<any> & {
onResize: (
e: React.SyntheticEvent<Element>,
data: ResizeCallbackData
) => void
width: number
}
) => {
const { onResize, width, ...restProps } = props
if (!width) {
return <th {...restProps} />
}
return (
<Resizable
width={width}
height={0}
handle={
<span
className="react-resizable-handle"
onClick={(e) => {
e.stopPropagation()
}}
/>
}
onResize={onResize}
draggableOpts={{ enableUserSelectHack: false }}
>
<th {...restProps} />
</Resizable>
)
}
const withResizableColumns = (WrappedComponent: React.ComponentType<any>) => {
return (props: WithResizableColumnsProps) => {
const { columns: initialColumns } = props
const [columns, setColumns] = useState(initialColumns)
useEffect(() => {
setColumns(initialColumns)
}, [initialColumns])
const handleResize =
(index: number) =>
(
_: React.SyntheticEvent<Element>,
{ size }: ResizeCallbackData
) => {
const newColumns = [...columns]
newColumns[index] = {
...newColumns[index],
width: size.width,
}
setColumns(newColumns)
}
const mergedColumns = columns.map((col, index) => ({
...col,
onHeaderCell: (column: any) => ({
width: column.width,
onResize: handleResize(index) as React.ReactEventHandler<any>,
}),
}))
return (
<WrappedComponent
{...props}
columns={mergedColumns}
components={{
header: {
cell: ResizableTitle,
},
}}
className={styles.resizableTableContainer}
/>
)
}
}
export default withResizableColumns
卡顿现象
在使用 withResizableColumns
高阶组件来实现列拖拽调整宽度时,确实可能会遇到性能卡顿的问题,尤其是在处理复杂的列或大量数据时。
性能问题通常与以下因素有关:
- 频繁的重新渲染:每次列宽调整都会触发组件的重新渲染。
- 复杂的列配置:复杂的列设置可能会导致性能下降。
- React 组件的复杂性:复杂的组件树和事件处理可能会增加渲染的负担。
提升性能的优化方法
下面是一些提升 withResizableColumns
性能的建议和优化措施:
1. 使用 React.memo
或 PureComponent
确保组件在列宽调整时不会不必要地重新渲染。使用 React.memo
或 PureComponent
来优化 ResizableTitle
组件的性能。
jsx
import React, { memo } from 'react';
import { Resizable } from 'react-resizable';
// 使用 React.memo 优化 ResizableTitle 组件
const ResizableTitle = memo((props) => {
const { onResize, width, ...restProps } = props;
if (!width) {
return <th {...restProps} />;
}
return (
<Resizable
width={width}
height={0}
handle={
<span
className="react-resizable-handle"
onClick={(e) => {
e.stopPropagation();
}}
/>
}
onResize={onResize}
draggableOpts={{ enableUserSelectHack: false }}
>
<th {...restProps} />
</Resizable>
);
});
export default ResizableTitle;
2. 最小化重新渲染
确保只有在列宽真正变化时才更新状态。可以通过 shouldComponentUpdate
或 React.memo
来优化渲染逻辑。
3. 使用虚拟化
对于非常大的数据集,可以考虑使用虚拟化技术(如 react-virtualized
或 react-window
)来提升表格的性能。虚拟化技术可以显著减少 DOM 节点的数量,提高渲染性能。
4. 性能监测和优化
使用 React 开发者工具和性能监测工具来识别性能瓶颈,并有针对性地优化。
改进后的 withResizableColumns
示例
综合考虑以上优化措施,这里是改进后的 withResizableColumns
示例:
jsx
import React, { useState, useEffect, memo } from 'react';
import { Resizable } from 'react-resizable';
import { debounce } from 'lodash';
import styles from './index.module.less';
interface WithResizableColumnsProps {
columns: any[];
}
const ResizableTitle = memo((props: {
onResize: (e: React.SyntheticEvent<Element>, data: ResizeCallbackData) => void;
width: number;
}) => {
const { onResize, width, ...restProps } = props;
if (!width) {
return <th {...restProps} />;
}
return (
<Resizable
width={width}
height={0}
handle={
<span
className="react-resizable-handle"
onClick={(e) => {
e.stopPropagation();
}}
/>
}
onResize={onResize}
draggableOpts={{ enableUserSelectHack: false }}
>
<th {...restProps} />
</Resizable>
);
});
const withResizableColumns = (WrappedComponent: React.ComponentType<any>) => {
return (props: WithResizableColumnsProps) => {
const { columns: initialColumns } = props;
const [columns, setColumns] = useState(initialColumns);
useEffect(() => {
setColumns(initialColumns);
}, [initialColumns]);
const handleResize = (index: number) => debounce((_, { size }) => {
const newColumns = [...columns];
newColumns[index] = {
...newColumns[index],
width: size.width,
};
setColumns(newColumns);
}, 100); // 防抖处理
const mergedColumns = columns.map((col, index) => ({
...col,
onHeaderCell: (column: any) => ({
width: column.width,
onResize: handleResize(index) as React.ReactEventHandler<any>,
}),
}));
return (
<WrappedComponent
{...props}
columns={mergedColumns}
components={{
header: {
cell: ResizableTitle,
},
}}
className={styles.resizableTableContainer}
/>
);
};
};
export default withResizableColumns;
总结
通过使用 React.memo
、防抖处理、最小化重新渲染、虚拟化以及性能监测等优化技术,可以显著提高列宽调整操作的性能和流畅度。确保高阶组件和表格组件在处理复杂场景时表现良好。