当前位置: 首页 > news >正文

react实现虚拟列表

在前端开发中,当一次性渲染大量数据时,直接渲染所有DOM节点,会造成渲染过慢,浏览器卡顿的现象,导致用户体验不佳,为了改善这种情况,提出使用虚拟列表的方式进行渲染。
虚拟列表的实现思路
1.只渲染可见区域:
计算当前可见区域的起始索引和结束索引。
只渲染可见区域内的列表项,其他区域用空白占位。
2.动态计算高度:
如果列表项高度固定,可以直接计算。
如果列表项高度不固定,需要动态计算每个列表项的高度。
3.滚动时更新渲染:
监听滚动事件,动态更新可见区域的列表项。
实现步骤
以下是实现虚拟列表的关键步骤:

  1. 固定高度的虚拟列表
    假设列表项高度固定,实现较为简单。
  2. 动态高度的虚拟列表
    列表项高度不固定,需要动态计算。
    代码实现
    固定高度的虚拟列表
    以下是一个固定高度的虚拟列表实现:
import React, { useState, useRef, useCallback } from "react";

const VirtualList = ({ data, itemHeight, visibleCount }) => {
  const [startIndex, setStartIndex] = useState(0); // 起始索引
  const containerRef = useRef(null); // 容器引用

  // 计算可见区域的数据
  const visibleData = data.slice(startIndex, startIndex + visibleCount);

  // 处理滚动事件
  const handleScroll = useCallback(() => {
    if (containerRef.current) {
      const scrollTop = containerRef.current.scrollTop;
      const newStartIndex = Math.floor(scrollTop / itemHeight);
      setStartIndex(newStartIndex);
    }
  }, [itemHeight]);

  return (
    <div
      ref={containerRef}
      style={{
        height: `${visibleCount * itemHeight}px`,
        overflow: "auto",
        border: "1px solid #ccc",
      }}
      onScroll={handleScroll}
    >
      <div
        style={{
          height: `${data.length * itemHeight}px`,
          position: "relative",
        }}
      >
        {visibleData.map((item, index) => (
          <div
            key={startIndex + index}
            style={{
              position: "absolute",
              top: `${(startIndex + index) * itemHeight}px`,
              width: "100%",
              height: `${itemHeight}px`,
              boxSizing: "border-box",
              borderBottom: "1px solid #eee",
            }}
          >
            {item}
          </div>
        ))}
      </div>
    </div>
  );
};

export default VirtualList;

使用示例

import React from "react";
import VirtualList from "./VirtualList";

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

  return (
    <div>
      <h1>Virtual List Example</h1>
      <VirtualList data={data} itemHeight={50} visibleCount={10} />
    </div>
  );
};

export default App;

动态高度的虚拟列表
如果列表项高度不固定,需要动态计算高度并缓存。以下是一个简单的实现思路:
初始化时测量高度:
渲染一个隐藏的列表项,测量其高度并缓存。
滚动时动态更新:
根据缓存的高度计算可见区域的起始索引和结束索引。

import React, { useState, useRef, useCallback, useEffect } from "react";

const DynamicVirtualList = ({ data, visibleCount }) => {
  const [startIndex, setStartIndex] = useState(0);
  const [heights, setHeights] = useState([]); // 缓存高度
  const containerRef = useRef(null);
  const itemRefs = useRef([]); // 列表项引用

  // 初始化时测量高度
  useEffect(() => {
    const newHeights = itemRefs.current.map(
      (ref) => ref?.getBoundingClientRect().height || 0
    );
    setHeights(newHeights);
  }, [data]);

  // 计算可见区域的数据
  const visibleData = data.slice(startIndex, startIndex + visibleCount);

  // 计算总高度
  const totalHeight = heights.reduce((sum, height) => sum + height, 0);

  // 计算起始偏移量
  const offset = heights.slice(0, startIndex).reduce((sum, height) => sum + height, 0);

  // 处理滚动事件
  const handleScroll = useCallback(() => {
    if (containerRef.current) {
      const scrollTop = containerRef.current.scrollTop;
      let newStartIndex = 0;
      let sum = 0;
      while (sum + heights[newStartIndex] < scrollTop) {
        sum += heights[newStartIndex];
        newStartIndex++;
      }
      setStartIndex(newStartIndex);
    }
  }, [heights]);

  return (
    <div
      ref={containerRef}
      style={{
        height: "500px",
        overflow: "auto",
        border: "1px solid #ccc",
      }}
      onScroll={handleScroll}
    >
      <div style={{ height: `${totalHeight}px`, position: "relative" }}>
        {visibleData.map((item, index) => (
          <div
            key={startIndex + index}
            ref={(el) => (itemRefs.current[startIndex + index] = el)}
            style={{
              position: "absolute",
              top: `${offset}px`,
              width: "100%",
              boxSizing: "border-box",
              borderBottom: "1px solid #eee",
            }}
          >
            {item}
          </div>
        ))}
      </div>
    </div>
  );
};

export default DynamicVirtualList;

使用示例

import React from "react";
import DynamicVirtualList from "./DynamicVirtualList";

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

  return (
    <div>
      <h1>Dynamic Virtual List Example</h1>
      <DynamicVirtualList data={data} visibleCount={10} />
    </div>
  );
};

export default App;

相关文章:

  • C#语法基础总结
  • C语言刷题第六章(下)
  • 神策数据接入 DeepSeek,AI 赋能数据分析与智能运营
  • 【华为OD-E卷 -122 字符统计及重排 100分(python、java、c++、js、c)】
  • repo init 错误 Permission denied (publickey)
  • 算法刷题记录——LeetCode篇(4) [第301~400题](持续更新)
  • CI/CD构建与注意事项
  • Vue3-高级特性
  • 【微服务】如何用Azure容器应用Job处理异步HTTP API请求
  • docker安装redis
  • 深入理解 HTML 中的统一资源定位器(URL)
  • 无人机校企合作新方向:人才培养,生产研发一体化技术详解
  • Vue生命周期
  • 【一文读懂】RTSP与RTMP的异同点
  • 蓝桥杯备赛(基础语法3)
  • [蓝桥杯 2023 省 B] 飞机降落
  • Dubbo 深度解析
  • 【FLOYD+并查集】蓝桥杯算法提高 Degrees of Separation
  • CC45.【C++ Cont】STL中的哈希表及练习
  • 【Python 算法 1.线性枚举】
  • 空间站第八批科学实验样品返抵地球并交付科学家
  • 4月一二线城市新房价格环比上涨,沪杭涨幅居百城前列
  • 空调+零食助顶级赛马备战,上海环球马术冠军赛即将焕新登场
  • 证监会副主席王建军被查
  • 人物|德国新外长关键词:总理忠实盟友、外交防务专家、大西洋主义者
  • 央行4月开展12000亿元买断式逆回购操作