React点击事件后交互时间很高该怎么优化?

建利 阅读 34

我正在做一个待办事项列表,点击按钮会动态渲染1000条数据。发现每次点击后交互时间(Time to Interactive)显示有2-3秒延迟,页面明显卡顿。我用了PureComponent和shouldComponentUpdate,但效果不明显。

代码结构大概是这样的:


class TodoList extends React.PureComponent {
  state = { items: [] };

  handleClick = () => {
    const newItems = Array.from({length: 1000}, (_, i) => `Item ${i}`);
    this.setState({ items: newItems });
  };

  render() {
    return (
      <>
        <button onClick={this.handleClick}>加载数据</button>
        <ul>
          {this.state.items.map((item, index) => (
            <li key={index}>{item}</li>
          ))}
        </ul>
      </>
    );
  }
}

测试发现列表渲染时内存占用飙升,但状态更新逻辑应该没问题啊。是不是key设置有问题?或者有其他优化点没考虑到?

我来解答 赞 6 收藏
二维码
手机扫码查看
2 条解答
シ付楠
シ付楠 Lv1
这个问题我之前也遇到过,1000条数据直接渲染确实会卡,主要瓶颈在于DOM操作太密集了。

你的直觉对了一半,key用index在这个场景下其实不是主要问题,因为你是整体替换数据,不是增删改。真正的问题是React要一次性创建1000个真实的DOM节点并插入页面,这个操作本身就很耗时间。

我给你几个实用的优化方案。

第一个方案是用虚拟列表,只渲染可视区域内的元素。这是最彻底的解决办法,推荐用react-window这个库。

import { FixedSizeList } from 'react-window';

class TodoList extends React.PureComponent {
state = { items: [] };

handleClick = () => {
const newItems = Array.from({length: 1000}, (_, i) => Item ${i});
this.setState({ items: newItems });
};

Row = ({ index, style }) => (
{this.state.items[index]}

);

render() {
return (
<>

height={400}
itemCount={this.state.items.length}
itemSize={35}
width="100%"
>
{this.Row}


);
}
}


第二个方案如果你不想引入新库,可以用时间分片,把渲染任务拆成多批执行。用requestIdleCallback或者setTimeout都行。

class TodoList extends React.PureComponent {
state = { items: [], displayItems: [] };

handleClick = () => {
const newItems = Array.from({length: 1000}, (_, i) => Item ${i});
this.setState({ items: newItems, displayItems: [] }, () => {
this.renderInChunks(newItems);
});
};

renderInChunks = (items, startIndex = 0) => {
const chunkSize = 50;
const endIndex = Math.min(startIndex + chunkSize, items.length);
const newDisplayItems = items.slice(0, endIndex);

this.setState({ displayItems: newDisplayItems });

if (endIndex < items.length) {
requestIdleCallback(() => this.renderInChunks(items, endIndex));
}
};

render() {
return (
<>


    {this.state.displayItems.map((item, index) => (
  • {item}

  • ))}


);
}
}


还有一个细节,你现在的写法每次点击都会生成新的数组,如果连续点击可能会有问题。可以加个loading状态防止重复点击。

虚拟列表是最推荐的方案,渲染10000条都没压力。时间分片那个方案会有渐进式渲染的效果,用户体验上可能稍微好一点,但总耗时其实差不多。

另外你提到内存占用飙升,这个正常,1000个DOM节点本来就要占内存,虚拟列表能完美解决这个问题,因为永远只渲染屏幕可见的那几十个。
点赞 1
2026-03-02 14:04
Mc.馨然
Mc.馨然 Lv1
你这个问题挺典型的,动态渲染大量数据时确实容易出现性能问题。虽然用了 PureComponentshouldComponentUpdate,但这里还有几个优化点可以试试。

### 1. **key 的问题**
你的 key 是用索引 index 设置的,这在动态列表场景下是不推荐的。React 官方文档里明确提到,如果列表项顺序会变或者有增删操作,用索引会导致不必要的重渲染。你可以把每条数据的唯一标识作为 key,比如这样:

const newItems = Array.from({length: 1000}, (_, i) => ({ id: item-${i}, name: Item ${i} }));


然后在渲染时用 id 作为 key

{this.state.items.map(item => (
  • {item.name}

  • ))}


    这样更清晰,也避免了索引带来的性能问题。

    ---

    ### 2. **虚拟化列表**
    渲染 1000 条数据对浏览器来说负担确实有点大。即使用户看不到那么多内容,React 还是会尝试渲染所有的 DOM 节点,导致内存占用飙升和卡顿。建议你用虚拟化列表(virtualized list),只渲染当前视口内的数据。

    可以用现成的库,比如 react-window 或者 react-virtualized。简单举个例子,用 react-window 的话:

    import { FixedSizeList as List } from 'react-window';

    class TodoList extends React.PureComponent {
    state = { items: [] };

    handleClick = () => {
    const newItems = Array.from({ length: 1000 }, (_, i) => ({ id: item-${i}, name: Item ${i} }));
    this.setState({ items: newItems });
    };

    renderRow = ({ index, style }) => {
    const item = this.state.items[index];
    return (

    {item.name}

    );
    };

    render() {
    return (
    <>
    <button onClick={this.handleClick}>加载数据</button>
    <List
    height={400}
    width={300}
    itemCount={this.state.items.length}
    itemSize={35}
    >
    {this.renderRow}
    </List>
    </>
    );
    }
    }


    这样就只渲染视口内的数据,滚动时动态更新,性能提升非常明显。

    ---

    ### 3. **setState 的批量处理**
    虽然这里看起来不是主要问题,但还是提一下:setState 是异步的,如果需要频繁更新状态,可以考虑合并多次调用,减少渲染次数。不过你这个例子中一次更新 1000 条数据,应该没问题。

    ---

    总结一下,key 改为唯一标识,加上虚拟化列表,性能基本就能搞定。别看虚拟化简单,渲染大量数据的时候真香。希望这些改动对你有帮助!
    点赞 13
    2026-02-01 17:08