Material-ui TreeView 性能优化:避免未变更子树重复渲染
Material-UI TreeView 大型宽树性能优化方案
核心实现逻辑
利用不可变数据的引用不变特性,配合React.memo的浅比较跳过未变更子树的渲染,从根本上减少全量重渲染的开销。
具体落地步骤
- 数据源层使用不可变更新规则
每次修改节点(增删、改属性、改状态)时,仅重新生成「根节点到被修改节点」路径上的节点对象,其余未修改子树的节点直接复用原有对象引用。比如给示例中的F节点添加子节点X时,仅需要重新生成A、C、F、X四个节点,B、D、E节点完全复用原引用,无需任何改动。
如果不想手动写递归更新逻辑,可以使用Immer库,其produce方法生成的新数据天然符合不可变更新的特性,未改动的节点引用会保持不变。 - 封装带渲染缓存的自定义TreeItem组件
MUI原生TreeItem没有做渲染跳过的优化,父组件重渲染时会触发所有子节点的重渲染。你可以用React.memo封装自定义TreeItem,自定义比较函数仅校验核心渲染属性,无变更时直接跳过渲染:
import { TreeItem as MuiTreeItem } from '@mui/lab'; import { memo } from 'react'; const arePropsEqual = (prev, next) => { return ( prev.nodeId === next.nodeId && prev.label === next.label && prev.selected === next.selected && prev.expanded === next.expanded && // 子节点引用不变则无需向下渲染 prev.children === next.children ); }; const TreeItem = memo(({ node, ...rest }) => { return <MuiTreeItem nodeId={node.id} label={node.label} {...rest} />; }, arePropsEqual);
- 递归渲染逻辑直接复用节点引用
写树的递归渲染函数时,直接传入原节点对象即可,未修改的节点因为引用不变,会被memo直接跳过渲染:
const renderTreeNode = (node) => { return ( <TreeItem key={node.id} node={node}> {node.children?.map(renderTreeNode) ?? null} </TreeItem> ); };
- 超大量级节点补充虚拟滚动
如果节点总数超过1万,仅靠不可变数据优化仍然可能有性能压力,可以搭配虚拟滚动方案,仅渲染可视区域内的节点,无论总节点数多少,单次渲染的节点数始终维持在几十的量级,渲染耗时基本可以稳定在10ms以内。
优化效果
千级节点的常规修改操作(增删节点、切换状态等)的渲染耗时基本可以控制在20ms以内,完全满足性能要求。
内容的提问来源于stack exchange,提问作者jonenst
相关产品推荐
相关产品推荐

