You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Ant Design Tree组件展开时渲染缓慢问题排查求助

Ant Design Tree 组件性能优化问题

我基于create-react-app开发了一个包含Ant Design Tree组件的EntityBrowser,状态用Redux管理,即将上线生产环境。

通过排查发现,Tree组件在treeData数据量较大时,每次展开都会重新渲染所有TreeNode,导致性能急剧下降;但现在仅少量节点就卡顿到无法使用,动态添加节点、展开操作都受影响,已通过性能分析确认节点数量/数据量增加会拉长渲染时间。

已尝试移除onSelect、titleRender等非必要props,提取内联函数,仅获得小幅性能提升,未解决核心卡顿问题。


Tree组件代码

<Tree
    rootClassName={dark ? "dark-tree" : ""}
    style={{ borderRadius: "0" }}
    blockNode={true}
    onExpand={onExpand}
    onSelect={onSelect}
    selectedKeys={selectedKeys}
    expandedKeys={expandedKeys}
    treeData={treeData}
    titleRender={TreeNode}
/>

onExpand和onSelect处理逻辑

const onExpand = (expandedKeys) => {
    dispatch(entityBrowserActions.updateExpandedKeys(expandedKeys));
};

const onSelect = (selectedKeys, { event }) => {
    if (handleSelection) handleSelection(selectedKeys);
    dispatch(entityBrowserActions.updateSelectedKeys(selectedKeys));
};

treeData生成函数

const createTreeData = (entityStructure) => {
    return entityStructure.map((item) => {
        const itemSelectable = !selectedKeys.includes(item.key);
        let title = (<span>{item.name}</span>);
        let itemObj = {};
        let newName = (e) => onNameChange(e, item.key);
        let stopEdit = () => handleStopEdit(item.key);

        if (editable && editingEntities[item.key] == true) {
            title = (
                <Input
                    value={item.name}
                    onChange={ newName }
                    onBlur={ stopEdit }
                />
            );
        }

        if (item.children) {
            itemObj = {
                title,
                key: item.key,
                selectable: itemSelectable, // Selectable only if not already selected 
                checkable: false,
                children: createTreeData(item.children),
            };
        } else {
            itemObj = {
                title,
                key: item.key,
                selectable: itemSelectable, 
            }
        }
        return itemObj;
    });
};

titleRender渲染函数

const TreeNode = (value, record) => {
    const handleEdit = () => handleStartEdit(value.key);
    const handleClose = () => handleClosePopup(value.key, deletePopoverVisible[value.key]);
    const handleDeleteAndClose = () => {
        handleDelete(value.key);
        handleClosePopup(value.key, false);
    }

    return (
        <div className="tree-title">
            {value.title}
            {editable ? (
                <div style={{}}>
                    <Button
                        type="text"
                        style={{ padding: "2px 4px" }}
                        onClick={ handleEdit }>
                        <EditOutlined style={{ fontSize: "1.1rem" }} />
                    </Button>
                    <Popover
                        visible={ deletePopoverVisible[value.key] }
                        onVisibleChange={ handleClose }
                        content={
                            <div style={{ display: "flex", flexDirection: "column" }}>
                                <Button
                                    type="text"
                                    style={{ color: "red" }}
                                    onClick={ handleDeleteAndClose }>
                                    Delete
                                </Button>
                                <Button
                                    type="text"
                                    onClick={ handleClose }>
                                    Cancel
                                </Button>
                            </div>
                        }
                        trigger="click">
                        <Button type="text" style={{ padding: "2px 4px" }}>
                            <DeleteOutlined style={{ fontSize: "1.1rem" }} />
                        </Button>
                    </Popover>
                </div>
            ) : null}
        </div>
    );
};

针对性优化方案

1. 缓存treeData避免全量重生成

createTreeData每次渲染都会递归生成全新的treeData对象,且每个节点的title都是新的React元素,导致Tree无法复用现有节点,强制全量重渲染。

  • 优化实现:用useMemo缓存treeData,仅在依赖项变化时重新生成
    const treeData = useMemo(() => {
        const createTreeData = (entityStructure) => {
            // 原createTreeData逻辑不变
        };
        return createTreeData(entityStructure);
    }, [entityStructure, selectedKeys, editable, editingEntities, onNameChange, handleStopEdit]);
    

2. 缓存titleRender组件与事件函数

TreeNode每次渲染都会创建新的内联事件函数,返回的DOM结构每次都是新引用,导致节点频繁重渲染。

  • 优化实现:
    • 把TreeNode提取为独立纯组件,用React.memo包裹
    • 用useCallback缓存内部事件处理函数
    // 独立纯组件
    const TreeTitleNode = React.memo(({ value, editable, handleStartEdit, handleClosePopup, deletePopoverVisible, handleDelete }) => {
        const handleEdit = useCallback(() => handleStartEdit(value.key), [handleStartEdit, value.key]);
        const handleClose = useCallback(() => handleClosePopup(value.key, deletePopoverVisible[value.key]), [handleClosePopup, value.key, deletePopoverVisible]);
        const handleDeleteAndClose = useCallback(() => {
            handleDelete(value.key);
            handleClosePopup(value.key, false);
        }, [handleDelete, handleClosePopup, value.key]);
    
        return (
            <div className="tree-title">
                {value.title}
                {editable ? (
                    <div>
                        <Button
                            type="text"
                            style={{ padding: "2px 4px" }}
                            onClick={handleEdit}>
                            <EditOutlined style={{ fontSize: "1.1rem" }} />
                        </Button>
                        <Popover
                            visible={deletePopoverVisible[value.key]}
                            onVisibleChange={handleClose}
                            content={
                                <div style={{ display: "flex", flexDirection: "column" }}>
                                    <Button
                                        type="text"
                                        style={{ color: "red" }}
                                        onClick={handleDeleteAndClose}>
                                        Delete
                                    </Button>
                                    <Button
                                        type="text"
                                        onClick={handleClose}>
                                        Cancel
                                    </Button>
                                </div>
                            }
                            trigger="click">
                            <Button type="text" style={{ padding: "2px 4px" }}>
                                <DeleteOutlined style={{ fontSize: "1.1rem" }} />
                            </Button>
                        </Popover>
                    </div>
                ) : null}
            </div>
        );
    });
    
    // 原组件中使用缓存后的函数
    const TreeNode = useCallback((value, record) => {
        return <TreeTitleNode 
            value={value} 
            editable={editable} 
            handleStartEdit={handleStartEdit} 
            handleClosePopup={handleClosePopup}
            deletePopoverVisible={deletePopoverVisible}
            handleDelete={handleDelete}
        />;
    }, [editable, handleStartEdit, handleClosePopup, deletePopoverVisible, handleDelete]);
    

3. 优化Redux状态更新逻辑

onExpand和onSelect直接dispatch新数组,可能导致组件因引用变化触发不必要重渲染。

  • 优化实现:在reducer中仅当内容变化时更新状态
    case UPDATE_EXPANDED_KEYS:
        const newExpandedKeys = action.payload;
        // 用lodash.isEqual或自定义函数比较数组内容
        if (JSON.stringify(state.expandedKeys) === JSON.stringify(newExpandedKeys)) {
            return state;
        }
        return { ...state, expandedKeys: newExpandedKeys };
    

4. 启用虚拟滚动(核心优化)

Ant Design Tree支持虚拟滚动,仅渲染可视区域内的节点,大幅减少渲染压力。

  • 优化实现:给Tree组件添加virtual属性并设置高度
    <Tree
        // 其他属性不变
        virtual={true}
        style={{ height: 600, overflow: 'auto' }}
    />
    

5. 减少selectable的重计算触发

selectedKeys.includes(item.key)会让所有节点依赖整个selectedKeys数组,数组变化时所有节点都会重计算selectable。

  • 优化实现:把selectable判断移到TreeTitleNode内部,或用useMemo缓存单个节点的选中状态判断结果。

内容的提问来源于stack exchange,提问作者Mads Garff

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 03:10:56