如何在React Virtualized的可展开面板中虚拟化内部行?
解决React Virtualized可展开面板内子列表的虚拟化优化问题
针对外层虚拟化列表中,可展开面板一次性加载500+子行导致UI卡顿的问题,核心是让外层列表正确感知展开行的总高度,同时对子列表做虚拟化处理,以下是具体实现方案:
关键思路
外层List需要动态计算每行的高度(区分折叠/展开状态),展开时要包含子列表的总高度;子列表用List组件实现虚拟化,且禁用自身滚动,完全由外层列表控制滚动逻辑,确保仅渲染可见区域的子行。
具体实现步骤
1. 引入CellMeasurer缓存父项高度
使用CellMeasurer和CellMeasurerCache缓存折叠状态下父项的高度,避免重复计算:
import { List, CellMeasurer, CellMeasurerCache } from 'react-virtualized'; // 初始化缓存,defaultHeight为父项折叠时的高度 const cache = new CellMeasurerCache({ defaultHeight: 50, fixedWidth: true });
2. 动态计算外层List的行高
外层List的rowHeight改为函数,根据行的展开状态返回对应高度:
// 子行固定高度(根据实际业务调整) const CHILD_ROW_HEIGHT = 30; const getRowHeight = ({ index }) => { const item = listItems[index]; if (item.isExpanded) { // 展开时高度 = 父项高度 + 子列表总高度(子行数×子行高度) return cache.getHeight(index) + item.children.length * CHILD_ROW_HEIGHT; } // 折叠时仅返回父项高度 return cache.getHeight(index); };
3. 实现父项渲染逻辑(包含虚拟化子列表)
在父项的rowRenderer中,展开时嵌套子List,并禁用其自身滚动:
const renderParentRow = ({ index, key, style }) => { const item = listItems[index]; const toggleExpand = () => { // 更新列表项的展开状态 const updatedItems = [...listItems]; updatedItems[index].isExpanded = !updatedItems[index].isExpanded; setListItems(updatedItems); }; return ( <CellMeasurer cache={cache} columnIndex={0} key={key} rowIndex={index}> <div style={style}> {/* 展开/折叠切换按钮 */} <div onClick={toggleExpand} style={{ padding: '10px', cursor: 'pointer' }}> {item.isExpanded ? '▼' : '▶'} {item.title} </div> {/* 虚拟化子列表 */} {item.isExpanded && ( <List width={style.width} // 继承父项宽度 height={item.children.length * CHILD_ROW_HEIGHT} // 子列表总高度 rowCount={item.children.length} rowHeight={CHILD_ROW_HEIGHT} rowRenderer={({ index: childIndex, style: childStyle }) => ( <div style={{ ...childStyle, padding: '8px' }}> {item.children[childIndex]} </div> )} scrollEnabled={false} // 禁用子列表滚动,由外层控制 /> )} </div> </CellMeasurer> ); };
4. 外层List的最终配置
将动态行高和父项渲染函数传入外层List:
<List width={window.innerWidth} height={window.innerHeight} rowCount={listItems.length} rowHeight={getRowHeight} rowRenderer={renderParentRow} />
核心优化点说明
- 高度计算准确性:通过预计算子列表总高度,让外层
List能正确计算滚动范围,避免滚动时出现内容跳跃或渲染异常。 - 子列表虚拟化:子
List仅渲染当前可见区域的子行,即使有500+条数据,也只会渲染屏幕内的行数,大幅减少DOM节点数量。 - 缓存机制:
CellMeasurerCache缓存父项高度,避免每次渲染都重新测量,提升性能。
内容的提问来源于stack exchange,提问作者Aman Sadhwani
相关产品推荐
相关产品推荐

