antd-table 大数据展开收缩性能问题

1,306 阅读2分钟

使用antd-table 在实际开发中伸缩或者展开的性能问题分析: 在面对1000+ 数据的table数据的话,直接全部伸缩或者展开的话,响应的效果一般会迟钝5000左右,针对性能分析图可以了解到,其中主要来自渲染方面就有2秒上,还有就是底层事件的调用,性能相对来说很差

- 解决方案:

  1. 使用高级组件react-virtual虚拟列表的处理来渲染可视区,来优化性能

  2. 给table一定高度,每一行的高度来滚动加载动态渲染优化性能

参考 ali-react-table:高性能 React 表格组件(zhuanlan.zhihu.com/p/130755755 )

    import {

BaseTable,

collectNodes,

features,

isLeafNode,

useTablePipeline

} from "ali-react-table";

import { Dropdown, Menu } from "@alifd/next";

import React, { useState, useContext, useEffect, useRef } from "react";

import ReactDOM from "react-dom";

import * as fusion from "@alifd/next";

import styled from "styled-components";

import { Button } from "@alifd/next";

\


import { Input, Form, InputRef } from "antd";

\


const EditableContext = React.createContext(null);

\


const EditableRow = ({ index, ...props }) => {

const [form] = Form.useForm();

return (

<Form form={form} component={false}>

<EditableContext.Provider value={form}>

<tr {...props} />

</EditableContext.Provider>

</Form>

);

};

\


const EditableCell = ({

title,

editable,

children,

dataIndex,

record,

handleSave,

...restProps

}) => {

const [editing, setEditing] = useState(false);

const inputRef = useRef < InputRef > null;

const form = useContext(EditableContext);

\


useEffect(() => {

if (editing) {

inputRef.current.focus();

}

}, [editing]);

\


const toggleEdit = () => {

debugger;

setEditing(!editing);

form.setFieldsValue({ [dataIndex]: record[dataIndex] });

};

\


const save = async () => {

try {

const values = await form.validateFields();

\


toggleEdit();

handleSave({ ...record, ...values });

} catch (errInfo) {

console.log("Save failed:", errInfo);

}

};

\


let childNode = children;

\


if (editable) {

childNode = editing ? (

<Form.Item

style={{ margin: 0 }}

name={dataIndex}

rules={[

{

required: true,

message: `${title} is required.`

}

]}

>

<Input ref={inputRef} onPressEnter={save} onBlur={save} />

</Form.Item>

) : (

<div

className="editable-cell-value-wrap"

style={{ paddingRight: 24 }}

onClick={toggleEdit}

>

{children}

</div>

);

}

\


return <td {...restProps}>{childNode}</td>;

};

\


const OperationsDiv = styled.div`

display: flex;

height: 20px;

align-items: center;

\


.item {

height: 20px;

cursor: pointer;

color: #3858cf;

display: flex;

align-items: center;

\


&.danger {

color: #eb4141;

}

}

\


.sep {

height: 10px;

width: 1px;

margin-left: 12px;

margin-right: 12px;

background: #eeeeee;

}

`;

\


function renderOptions() {

return (

<OperationsDiv>

<div className="item">编辑</div>

<div className="sep" />

<div className="item danger">删除</div>

<div className="sep" />

\


<Dropdown trigger={<div className="item">更多</div>} triggerType="click">

<Menu>

<Menu.Item>Option 1</Menu.Item>

<Menu.Item>Option 2</Menu.Item>

<Menu.Item>Option 3</Menu.Item>

<Menu.Item>Option 4</Menu.Item>

</Menu>

</Dropdown>

</OperationsDiv>

);

}

\


export const operationCol = {

lock: true,

name: "操作",

render: renderOptions,

width: 200

};

\


export const dataSource = [

{

id: "1",

title: "一级标题",

dept: "消费者事业部-淘宝-UED",

dest: "South Maddison",

guide: "Don Moreno",

children: makeChildren("1")

},

{

id: "2",

title: "一级标题",

dept: "航旅事业部-酒店业务",

dest: "Emilhaven",

guide: "Douglas Richards",

children: makeChildren("2")

},

{

id: "3",

title: "一级标题",

dept: "消费者事业部-淘宝-UED",

dest: "云南大理",

guide: "Douglas Lee",

children: makeChildren("3")

},

{

id: "4",

title: "一级标题",

dept: "信息平台部-用户体验部",

dest: "杭州千岛湖",

guide: "Eric Castillo",

children: makeChildren("4")

},

{

id: "5",

title: "一级标题",

dept: "消费者事业部-淘宝-UED",

dest: "East Karl",

guide: "Herbert Patton"

}

];

\


export function makeChildren(prefix) {

return [

{

id: `${prefix}-1`,

title: "二级标题",

dept: "消费者事业部-淘宝-UED",

dest: "云南大理",

guide: "Douglas Lee",

children: [

{

id: `${prefix}-1-1`,

title: "三级标题",

dept: "盒马产品技术部-UED",

dest: "云南大理",

guide: "Douglas Lee"

},

{

id: `${prefix}-1-2`,

title: "三级标题",

dept: "盒马产品技术部-前端",

dest: "云南大理",

guide: "Douglas Lee"

}

]

},

{

id: `${prefix}-2`,

title: "二级标题",

dept: "消费者事业部-淘宝-UED",

dest: "云南大理",

guide: "Douglas Lee",

children: [

{

id: `${prefix}-2-1`,

title: "三级标题",

dept: "盒马产品技术部-UED",

dest: "云南大理",

guide: "Douglas Lee"

},

{

id: `${prefix}-2-2`,

title: "三级标题",

dept: "盒马产品技术部-前端",

dest: "云南大理",

guide: "Douglas Lee"

}

]

},

{

id: `${prefix}-3`,

title: "二级标题",

dept: "消费者事业部-淘宝-UED",

dest: "云南大理",

guide: "Douglas Lee"

}

];

}

\


function BaseAliTable() {

const [openKeys, onChangeOpenKeys] = useState(["4", "4-2"]);

\


const columns = [

{ code: "title", name: "标题", width: 200 },

{ code: "dept", name: "部门名称", width: 180, editable: true },

{ code: "dest", name: "团建目的地", width: 160 },

{ code: "guide", name: "当地导游", width: 160 },

operationCol

];

\


const pipeline = useTablePipeline({ components: fusion })

.input({ dataSource: dataSource, columns: columns })

.primaryKey("id")

.use(features.treeMode({ openKeys, onChangeOpenKeys }));

\


const allParentKeys = collectNodes(dataSource, "pre")

.filter((row) => !isLeafNode(row))

.map((row) => row.id);

\


return (

<div>

<Button.Group>

<Button onClick={() => onChangeOpenKeys(allParentKeys)}>

展开全部

</Button>

<Button onClick={() => onChangeOpenKeys([])}>收拢全部</Button>

</Button.Group>

<BaseTable

{...pipeline.getProps()}

components={{ row: EditableRow, cell: EditableCell }}

/>

</div>

);

}

ReactDOM.render(<BaseAliTable />, document.getElementById("container"));

\

编辑不行

参考: github.com/ant-design/…

这个例子会出现一些问题: 例如拿不到offsetHeight,主要原因就是row的current为空,使用bind绑定示例可以获取,另外就是编辑的body就没有办法传进去

  1. 进入不到body.cell设置

image.png 此时拿到的arg为[]

image.png 进入不到设置body.cell里面去,导致没有办法进行 2.拿不到offsetHeight,主要原因就是row的current为空

image.png

解决方案: 查找出区别,重写set方法,但是代码里面用的函数组件没有办法去绑定实例(主要是this指向问题导致)

image.png

antd大数据渲染

其次就是antd-table 编辑单元格,可以看antd-table 编辑单元格的例子,但是antd的例子不适用于编辑类表格

image.png

image.png

最后解决方案: 1. 可以自己手写一个虚拟列表或者分页加载,可视区的高度以及每行高度来进行判断要加载多少 2. 内部处理编辑替换单元格事件,再利用virtualizedtableforantd4事件去设置components = {vt},如果数据量较少,可以传一个标识符来判断是否进行component = {isNeedComponent ? components : vt}