React

关注公众号 jb51net

关闭
首页 > 网络编程 > JavaScript > javascript类库 > React > react大列表优化

react大列表实现优化

作者:光影少年

React 大列表优化中,是最常见、最有效的优化方案之一,本文就来介绍一下大列表如何实现优化,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧<BR>

React 大列表优化中,虚拟列表(Virtual List) 是最常见、最有效的优化方案之一。

为什么需要虚拟列表

假设有 10 万条数据:

{
  data.map(item => (
    <Row key={item.id} {...item} />
  ))
}

React 会一次性生成:

<div>...</div>
<div>...</div>
...
(100000个节点)

问题:

即使用户当前只能看到 20 条数据,浏览器仍然维护 10 万个 DOM。

虚拟列表核心思想

只渲染可视区域内的数据。

例如:

总数据:100000条

当前屏幕:
----------------
第100条
第101条
...
第120条
----------------

实际上只渲染:

100 ~ 120

共 20 个节点。

滚动时:

向下滚动
100~120
↓
110~130

销毁旧节点,复用或创建新节点。

DOM 数量始终维持:

20 ~ 30个

而不是:

100000个

实现原理

假设:

总数 = 100000
每行高度 = 50px

总高度:

100000 * 50
= 5000000px

容器:

<div className="container">
  <div className="phantom"></div>
  <div className="content"></div>
</div>

结构:

container
│
├── phantom
│     高度5000000px
│
└── content
      真正渲染的数据

1. 创建占位元素(phantom)

<div
  style={{
    height: data.length * itemHeight
  }}
/>

作用:

让滚动条看起来像真的有 10 万条数据。

滚动条长度正常

但实际上没有渲染全部节点。

2. 监听滚动

const handleScroll = (e) => {
  const scrollTop = e.target.scrollTop;
};

例如:

scrollTop = 5000px

3. 计算开始索引

公式:

startIndex = Math.floor(
  scrollTop / itemHeight
);

例如:

5000 / 50
= 100

说明:

当前从第100条开始显示

4. 计算结束索引

可视区域:

containerHeight = 500px

可显示:

500 / 50

= 10条

结束索引:

endIndex = startIndex + visibleCount;
100 + 10
= 110

5. 截取数据

const visibleData =
  data.slice(
    startIndex,
    endIndex
  );

只渲染:

{
  visibleData.map(...)
}

6. 偏移内容位置

如果直接渲染:

100~110

会出现在顶部。

需要移动到正确位置:

offsetY =
  startIndex * itemHeight;
100 * 50
= 5000px
<div
  style={{
    transform: `translateY(${offsetY}px)`
  }}
>

最终效果:

滚动5000px

↓
显示第100条

图解

总高度 5000000px
┌──────────────────┐
│                  │
│ phantom          │
│                  │
└──────────────────┘
      ↑
当前滚动到这里
      ↓
translateY(5000px)
┌──────────────────┐
│ 第100条          │
│ 第101条          │
│ 第102条          │
│ ...              │
└──────────────────┘

简易实现

function VirtualList() {
  const itemHeight = 50;
  const visibleCount = 10;

  const [start, setStart] = useState(0);

  const handleScroll = (e) => {
    const scrollTop = e.target.scrollTop;

    setStart(
      Math.floor(scrollTop / itemHeight)
    );
  };

  const visibleData = data.slice(
    start,
    start + visibleCount
  );

  return (
    <div
      style={{
        height: 500,
        overflow: "auto"
      }}
      onScroll={handleScroll}
    >
      <div
        style={{
          height:
            data.length * itemHeight,
          position: "relative"
        }}
      >
        <div
          style={{
            transform: `translateY(${
              start * itemHeight
            }px)`
          }}
        >
          {visibleData.map(item => (
            <div
              key={item.id}
              style={{
                height: itemHeight
              }}
            >
              {item.name}
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

优化:缓冲区(Buffer)

实际项目不会只渲染可视区域。

例如:

可视区域:10条
上缓冲:5条
下缓冲:5条

实际渲染:

20条

这样滚动时不会频繁白屏。

const buffer = 5;

start =
  Math.max(
    0,
    startIndex - buffer
  );

end =
  startIndex +
  visibleCount +
  buffer;

动态高度怎么办?

上面算法要求:

固定高度

如果每一项高度不同:

50px
80px
120px
60px
...

就不能简单用:

scrollTop / itemHeight

需要维护:

高度缓存表

例如:

[
  50,
  130,
  250,
  370,
  ...
]

通过二分查找定位:

scrollTop
↓
对应哪一项

这就是很多虚拟列表库复杂的地方。

React常用虚拟列表库

1.react-window 官网

作者:Brian Vaughn

特点:

npm install react-window

2.react-virtualized 官网

特点:

缺点:

3.TanStack Virtual 官网

特点:

面试回答模板

当面试官问:

React 大列表如何优化?

可以这样回答:

大列表的性能瓶颈主要在于 DOM 数量过多导致的渲染、重排和内存开销。常见方案是使用虚拟列表。虚拟列表的核心思想是只渲染当前可视区域的数据,而不是全部数据。通过监听 scroll 事件,根据 scrollTop 计算 startIndex 和 endIndex,仅渲染这一段数据,同时利用一个等高占位元素撑开滚动高度,并通过 translateY 将当前渲染内容移动到正确位置。这样无论数据量是 1 万条还是 10 万条,页面中实际 DOM 数量都保持在几十个,从而大幅提升性能。

到此这篇关于react大列表实现优化的文章就介绍到这了,更多相关react大列表优化内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文