长列表滚动卡顿,怎么优化内存占用?
我用 React 渲染一个上千条消息的聊天列表,虽然用了虚拟滚动,但内存还是涨得特别快,页面越滚越卡。是不是没清理掉不可见的 DOM 节点?
目前是用 react-window 的 FixedSizeList,每个 item 是个函数组件,里面还用了 useMemo 包裹内容。但 Chrome 内存快照里看到很多 detached DOM 节点,不知道是不是哪里引用没释放。
关键代码大概这样:
const MessageItem = React.memo(({ data, index }) => {
const message = data[index];
return (
<div className="message">
{message.text}
</div>
);
});
// 列表渲染
<FixedSizeList
height={600}
itemCount={messages.length}
itemSize={60}
itemData={messages}
>
{MessageItem}
</FixedSizeList>
首先检查下MessageItem组件里有没有副作用没清理,比如事件监听器、定时器之类的。用useEffect的话必须带清理函数:
其次看下消息数据结构,如果每条消息都带大体积的附件数据,即使用了memo也扛不住。建议把消息体拆成两部分,列表只渲染必要字段,详情数据等点击展开时再加载。
react-window的itemData如果频繁更新也可能有问题。试试把messages用useMemo包一下:
还有个骚操作是强制垃圾回收,虽然不优雅但能应急:
最后终极方案是上时间分片,把渲染压力分摊到多个帧。不过这个改起来比较麻烦,先把前面几点搞定应该就能缓解不少。
另外 itemData 如果每次渲染都是新数组引用,会导致所有 item 重渲染,内存肯定炸。
改这样:
detached DOM 节点多半是 style 没传导致虚拟滚动回收失效,或者你 item 里面有事件监听器没清理。