React动态高度列表滚动卡顿怎么优化?

司翰 阅读 24

在用React做商品列表时,每个列表项高度动态变化,滚动到300条左右就明显卡顿。尝试过用固定高度但内容溢出,用useState+useEffect实时计算总高度又导致渲染阻塞,有没有更好的解决办法?

现在用的是这种动态计算方式:


const [totalHeight, setTotalHeight] = useState(0);

useEffect(() => {
  const heights = items.map(item => 
    document.getElementById(<code>item-${item.id}</code>).offsetHeight
  );
  setTotalHeight(heights.reduce((a,b)=>a+b,0));
}, [items]);

但滚动时console.log发现频繁触发回流,fps掉到20以下

虚拟滚动库(如react-window)好像只能处理固定高度列表,换成弹性滚动方案又卡在计算item分布位置的公式上…

我来解答 赞 3 收藏
二维码
手机扫码查看
1 条解答
IT人智慧
动态高度列表的滚动性能优化确实是个麻烦事,按照规范的做法,建议你使用虚拟列表方案配合动态测量。虽然你说react-window只能处理固定高度,但它的可变高度模式其实能解决你的问题。

关键是用 VariableSizeList 组件,并实现 itemSize 的动态测量。这里有个完整的解决方案:

import { useState, useRef } from 'react';
import { VariableSizeList as List } from 'react-window';

function DynamicList({ items }) {
const [itemSizes, setItemSizes] = useState({});
const listRef = useRef(null);

// 创建测量缓存
const getSize = (index) => itemSizes[index] || 50; // 默认50px

// 测量实际高度
const measureRow = (index, element) => {
if (!element) return;
const height = element.offsetHeight;
if (height !== itemSizes[index]) {
setItemSizes(prev => ({ ...prev, [index]: height }));
listRef.current.resetAfterIndex(index);
}
};

return (
<List
ref={listRef}
height={600}
itemCount={items.length}
itemSize={getSize}
width={'100%'}
>
{({ index, style }) => (
<div
style={style}
ref={el => measureRow(index, el)}
>
{items[index].content}
</div>
)}
</List>
);
}


几点说明:第一,VariableSizeList 是专门处理动态高度的;第二,通过 resetAfterIndex 方法可以精确更新特定索引后的布局,避免全量重绘;第三,这种方式只会在首次渲染时有轻微回流,后续都从缓存读取高度。

如果你发现初始化还是有点卡,可以把测量逻辑放到 requestIdleCallback 里执行。另外记得给列表项设置 will-change: transform 样式,这能显著提升重绘性能。

最后吐槽一句,这种性能优化真是折磨人,不过按这个方案改完应该能稳定在60fps了。
点赞
2026-02-17 11:20