长列表滚动卡顿,怎么优化内存占用?

UX-艳玲 阅读 44

我用 React 渲染一个上千条消息的聊天列表,虽然用了虚拟滚动,但内存还是涨得特别快,页面越滚越卡。是不是没清理掉不可见的 DOM 节点?

目前是用 react-windowFixedSizeList,每个 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>
我来解答 赞 6 收藏
二维码
手机扫码查看
2 条解答
Dev · 馨月
这问题我遇到过,detached DOM节点堆积确实是个典型的内存泄漏迹象。虽然用了react-window和memo,但可能有几个点没处理好。

首先检查下MessageItem组件里有没有副作用没清理,比如事件监听器、定时器之类的。用useEffect的话必须带清理函数:
useEffect(() => {
const handler = () => {...};
window.addEventListener('resize', handler);
return () => window.removeEventListener('resize', handler);
}, []);


其次看下消息数据结构,如果每条消息都带大体积的附件数据,即使用了memo也扛不住。建议把消息体拆成两部分,列表只渲染必要字段,详情数据等点击展开时再加载。

react-window的itemData如果频繁更新也可能有问题。试试把messages用useMemo包一下:
const memoizedMessages = useMemo(() => messages, [messages.length]);


还有个骚操作是强制垃圾回收,虽然不优雅但能应急:
window.performance.memory && window.performance.memory.jsHeapSizeLimit > 0 
&& window.gc();


最后终极方案是上时间分片,把渲染压力分摊到多个帧。不过这个改起来比较麻烦,先把前面几点搞定应该就能缓解不少。
点赞
2026-03-07 10:03
Designer°国娟
我之前也遇到过这个问题,你这代码漏了关键的 style 属性传递,react-window 必须把 style 传给 item 组件才能正确回收 DOM。

另外 itemData 如果每次渲染都是新数组引用,会导致所有 item 重渲染,内存肯定炸。

改这样:

const MessageItem = React.memo(({ data, index, style }) => {
const message = data[index];
return (

{message.text}

);
});

// 用 useMemo 包住 messages 避免每次新引用
const memoizedMessages = useMemo(() => messages, [messages]);

height={600}
itemCount={memoizedMessages.length}
itemSize={60}
itemData={memoizedMessages}
>
{MessageItem}


detached DOM 节点多半是 style 没传导致虚拟滚动回收失效,或者你 item 里面有事件监听器没清理。
点赞
2026-03-02 14:01