动态高度长列表滚动时高度计算不准怎么办?
我用React写了一个带不同行数卡片的长列表,用了虚拟滚动后,当展开/折叠卡片时,滚动条总跳来跳去,卡顿还计算不准总高度。
尝试用ref手动测量每个卡片高度,把总高度存在state里,但更新时列表会突然跳动。代码大概是这样:
const [totalHeight, setTotalHeight] = useState(0);
useEffect(() => {
const heights = items.map(item =>
ReactDOM.findDOMNode(item.ref).offsetHeight
);
setTotalHeight(heights.reduce((a,b)=>a+b,0));
}, [items]);
这样每次数据变化都重新计算,性能反而更差了。有没有更好的动态高度计算方案?
react-virtualized或者react-window这类库来处理,它们对动态高度有专门的支持。代码给你:
关键点:
1. 用
react-window的VariableSizeList来支持动态高度。2. 每一行渲染时通过
onLoad或其他生命周期(比如点击展开/折叠)触发高度测量。3. 测量后更新对应索引的高度缓存,并调用
resetAfterIndex方法通知列表重新计算布局。4. 默认高度先给一个预估值(比如50),避免初次渲染时抖动。
注意:如果展开/折叠卡片是异步操作(比如动画),可以在动画结束后再调用
updateRowHeight,否则高度可能测量不准。这个方案性能不错,因为只会在需要时重新计算受影响的部分,而不是整个列表。写完记得测一下极端情况,比如快速连续展开多个卡片,看看会不会有延迟或者错位。
代码放这了:
重点是把每个item的高度缓存起来,只有当某个item实际变化时才更新它的高度。这样能大幅减少不必要的计算。
另外推荐你用react-window或react-virtualized这类库,它们专门处理动态高度的场景。比如react-window的VariableSizeList组件就很适合你这个需求,它内部已经帮你处理好高度测量和缓存了。
实在不行就直接上现成的库,别自己折腾了,这种轮子没必要重复造。我之前也踩过这个坑,最后还是乖乖用了现成方案,省心多了。