react虚拟列表实现及原理

好问题 👍

React 虚拟列表(Virtual List,也叫虚拟滚动 Virtual Scrolling)是 前端性能优化的核心技术之一,尤其在需要渲染上万条数据的表格或列表时很关键。下面我帮你拆解:


🔹 一、为什么要用虚拟列表?

  • 普通做法:一次性渲染所有 DOM(比如 1 万条 <div>),会让浏览器卡顿,首屏加载慢。
  • 虚拟列表:只渲染 可视区域的元素 ,其他部分用空白占位,随着滚动动态更新。
    👉 这样 DOM 数量始终保持在几十到几百之间,性能大幅提升。

🔹 二、基本原理

虚拟列表的核心思想是 "只渲染视口范围内的元素 + 占位保持滚动条高度"

  1. 计算容器高度

    • 根据数据总数 × 单项高度 = 总高度(生成一个大容器,撑起滚动条)。
  2. 计算可见区域

    • 监听滚动事件,得到 scrollTop
    • 根据 scrollTop / itemHeight 算出应该显示哪些元素。
  3. 渲染可见元素

    • absolute 定位,把可见元素放在容器的正确位置。
    • 其他元素不渲染,靠容器的 paddingtranslateY 占位。

🔹 三、React 简单实现示例

假设每行高度固定(50px),一共 10000 条数据。

ini 复制代码
import React, { useState, useRef } from "react";

const rowHeight = 50; // 每项高度
const total = 10000;  // 数据总数
const visibleCount = 10; // 可视区显示多少项(容器高度 / rowHeight)

const data = Array.from({ length: total }, (_, i) => `Item ${i + 1}`);

export default function VirtualList() {
  const [start, setStart] = useState(0);
  const containerRef = useRef<HTMLDivElement>(null);

  const handleScroll = () => {
    if (!containerRef.current) return;
    const scrollTop = containerRef.current.scrollTop;
    const newStart = Math.floor(scrollTop / rowHeight);
    setStart(newStart);
  };

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

  return (
    <div
      ref={containerRef}
      onScroll={handleScroll}
      style={{
        height: rowHeight * visibleCount,
        overflowY: "auto",
        border: "1px solid #ccc",
        position: "relative",
      }}
    >
      {/* 大容器:撑起滚动条 */}
      <div style={{ height: total * rowHeight, position: "relative" }}>
        {/* 可视区元素 */}
        {visibleData.map((item, i) => (
          <div
            key={item}
            style={{
              position: "absolute",
              top: (start + i) * rowHeight,
              height: rowHeight,
              lineHeight: `${rowHeight}px`,
              borderBottom: "1px solid #eee",
              boxSizing: "border-box",
              width: "100%",
            }}
          >
            {item}
          </div>
        ))}
      </div>
    </div>
  );
}

👉 效果:

  • 容器始终只有 ~10 个 DOM 元素。
  • 滚动时动态替换显示的元素。
  • 看起来就像 10000 条都在 DOM 里,但实际上只有少量渲染。

🔹 四、进阶优化

  1. 动态高度支持

    • 如果每项高度不同,需要预先计算累积高度(类似 "前缀和"),滚动时二分查找定位。
    • 比如 react-virtualizedCellMeasurer
  2. 缓冲区(overscan)

    • 在可视区域上下多渲染几条,避免快速滚动时白屏。
  3. 分片渲染(时间切片)

    • 避免一次性渲染太多(requestIdleCallback 或 React 18 startTransition)。

🔹 五、成熟方案

如果自己写太复杂,可以直接用社区库:

  • react-window(轻量,推荐,适合固定高度)
  • react-virtualized(功能强大,支持动态高度、表格、Grid)
  • rc-virtual-list(Ant Design 内部用的虚拟列表)
javascript 复制代码
import { FixedSizeList as List } from "react-window";

<List
  height={500}
  itemCount={10000}
  itemSize={50}
  width="100%"
>
  {({ index, style }) => (
    <div style={style}>Row {index}</div>
  )}
</List>

🔹 六、总结

  • 原理:可见区域渲染 + 容器占位 + 滚动时替换数据。
  • 优势:DOM 数量固定,内存和渲染性能提升明显。
  • 扩展:支持动态高度、缓冲区、时间切片。
  • 推荐库:react-window(简单)、react-virtualized(复杂场景)。

相关推荐
passerby606127 分钟前
完成前端时间处理的另一块版图
前端·github·web components
掘了34 分钟前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅37 分钟前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅1 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅1 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment1 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅2 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊2 小时前
jwt介绍
前端
爱敲代码的小鱼2 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax
Cobyte2 小时前
AI全栈实战:使用 Python+LangChain+Vue3 构建一个 LLM 聊天应用
前端·后端·aigc