如何为递归渲染的Material UI TreeView添加useCallback/useMemo做性能优化
Material UI TreeView 性能优化方案(基于useCallback/useMemo)
性能瓶颈说明
原有实现中renderTree函数会在组件每次重渲染时重新生成引用,递归渲染逻辑会导致所有TreeItem节点在父组件重渲时全部重新渲染,节点数量达到数百级时会出现明显卡顿、交互延迟问题。
核心优化步骤
- 用
React.memo封装独立的树节点组件,只有当节点属性发生变化时才会触发当前节点重渲染,阻断不必要的重渲染链路 - 用
useCallback缓存递归渲染函数,保证函数引用稳定,不会触发子组件的无效重渲染 - 用
useMemo缓存树结构数据源,避免数据源引用无意义变更导致的全量节点重渲染
优化后完整代码
import * as React from 'react'; import TreeView from '@mui/lab/TreeView'; import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import ChevronRightIcon from '@mui/icons-material/ChevronRight'; import TreeItem from '@mui/lab/TreeItem'; // 1. 用memo封装独立的树节点组件,浅比较props判断是否需要重渲 const MemoTreeItem = React.memo(({ node, renderTree }) => { return ( <TreeItem nodeId={node.id} label={node.name}> {Array.isArray(node.children) ? node.children.map((child) => renderTree(child)) : null} </TreeItem> ); }); // 固定的静态数据源可以直接放在组件外部,避免组件重渲时重复创建 const staticData = { id: 'root', name: 'Parent', children: [ { id: '1', name: 'Child - 1', }, { id: '3', name: 'Child - 3', children: [ { id: '4', name: 'Child - 4', }, ], }, ], }; export default function RichObjectTreeView() { // 2. 如果数据源是动态生成/接口返回的,用useMemo缓存固定引用 const treeData = React.useMemo(() => { // 动态数据处理逻辑写在这里,比如格式化接口返回的节点数据 return staticData; }, []); // 依赖项填会触发数据变更的变量,比如接口返回的原始数据 // 3. 用useCallback缓存递归渲染函数,固定引用 const renderTree = React.useCallback((node) => { return <MemoTreeItem key={node.id} node={node} renderTree={renderTree} />; }, []); return ( <TreeView aria-label="rich object" defaultCollapseIcon={<ExpandMoreIcon />} defaultExpanded={['root']} defaultExpandIcon={<ChevronRightIcon />} sx={{ height: 110, flexGrow: 1, maxWidth: 400, overflowY: 'auto' }} > {renderTree(treeData)} </TreeView> ); }
额外优化建议
- 节点的key必须使用稳定的唯一值(比如节点id),禁止使用数组索引作为key
- 节点数量超过500时可以搭配虚拟滚动方案,仅渲染可视区域内的节点,进一步降低渲染性能消耗
- 如果节点有复杂的自定义渲染内容,可以单独把渲染逻辑用useMemo缓存,避免每次重渲都重复生成DOM结构
内容的提问来源于stack exchange,提问作者Django
相关产品推荐
相关产品推荐

