利用React实现虚拟列表的示例代码

接下来我会为你详细讲解“利用React实现虚拟列表的示例代码”的攻略。

什么是虚拟列表

虚拟列表是一种优化长列表性能的方式,通常用于在前端渲染海量数据的场景。它的基本思路是只渲染可见区域的数据,而不是渲染整个列表内容。

如何利用React实现虚拟列表

以下是基于React实现虚拟列表的示例代码及其详细讲解:

步骤一:设置列表数据和列表项高度

根据虚拟列表的思路,首先需要确定显示列表的可见部分高度和单个列表项的高度。我们以一个10000个数据项的列表为例:

const ITEM_HEIGHT = 50; // 列表项高度
const TOTAL_COUNT = 10000; // 数据总数

// 构造数据源
const data = new Array(TOTAL_COUNT).fill('').map((_, index) => {
  return `第${index}项`;
});

步骤二:使用useRef和useState存储可视区域的数据

下一步,我们需要根据滚动位置来计算哪些数据需要被渲染。因为需要在render之间保存数据,我们需要使用React的useRef和useState来保存数据:

const [start, setStart] = useState(0); // 可视区域的起始位置
const [end, setEnd] = useState(10); // 可视区域的结束位置

// 使用useRef来保存DOM节点和scrollTop
const listRef = useRef(null); 
const scrollTopRef = useRef(0);

const visibleData = useMemo(() => {
  // 这里的slice是为了复制一份只包含可视区域的数据,避免直接使用data.slice(start, end)导致原始data被修改的问题
  return data.slice(start, end);
}, [start, end]);

步骤三:渲染列表项

现在我们可以将可视区域内的数据渲染出来了。在这里,我们遍历visibleData生成对应的列表项:

<div style={{ height: `${ITEM_HEIGHT * count}px` }}>
  {visibleData.map((item, index) => {
    return (
      <div key={start + index} style={{ 
        height: `${ITEM_HEIGHT}px`,
        position: 'absolute',
        top: `${(start + index) * ITEM_HEIGHT}px`,
        width: '100%',
      }}>
        {item}
      </div>
    );
  })}
</div>

步骤四:监听滚动事件,更新可视区域数据

为了实现滚动和更新可视区域数据,我们需要在列表外层包裹一个div,并给它设置固定高度和overflow: auto属性。通过监听这个div的scroll事件,我们可以根据它的scrollTop来计算可视区域的起始和结束位置:

const handleScroll = useCallback(
  throttle(() => {
    const listEl = listRef.current;
    const scrollTop = listEl.scrollTop;
    scrollTopRef.current = scrollTop;

    setStart(Math.floor(scrollTop / ITEM_HEIGHT));
    setEnd(Math.ceil((scrollTop + window.innerHeight) / ITEM_HEIGHT));
  }, 166), // 166ms 节流,避免在滚动过程中频繁计算
  []
);

<div ref={listRef} style={{ 
  height: '500px',  // 设置为可视区域高度即可
  overflow: 'auto',
}} onScroll={handleScroll}>
  <div style={{ height: `${ITEM_HEIGHT * TOTAL_COUNT}px` }}>
    {visibleData.map((item, index) => {
      return (
        <div key={start + index} style={{ 
          height: `${ITEM_HEIGHT}px`,
          position: 'absolute',
          top: `${(start + index) * ITEM_HEIGHT}px`,
          width: '100%',
        }}>
          {item}
        </div>
      );
    })}
  </div>
</div>

示例说明

以上是基于React实现虚拟列表的完整攻略,现在我们来看两个进一步的示例说明:

示例一:使用IntersectionObserver实现可视区域的监听

在上面的示例中,我们在scroll滚动事件中计算了可视区域的起始和结束位置。但是,这种做法会在scroll滚动时触发大量的事件,降低性能。为了避免这个问题,我们可以使用IntersectionObserver API来监听可视区域变化,减少事件的触发次数。以下是示例代码:

const [intersectingStart, setIntersectingStart] = useState(0); // 相交区域的起始位置
const [intersectingEnd, setIntersectingEnd] = useState(10); // 相交区域的结束位置

const handleObserveChange = useCallback((entries) => {
  for (let entry of entries) {
    const index = entry.target.dataset.index * 1;
    if (entry.isIntersecting) {
      if (index < intersectingStart) {
        setIntersectingStart(index);
      }
      if (index > intersectingEnd) {
        setIntersectingEnd(index);
      }
    } else {
      if (index === intersectingStart) {
        setIntersectingStart(index + 1);
      }
      if (index === intersectingEnd) {
        setIntersectingEnd(index - 1);
      }
    }
  }
}, [intersectingStart, intersectingEnd]);

<div ref={listRef} style={{ 
  height: '500px',
  overflow: 'auto',
}} onScroll={handleScroll}>
  <div ref={visibleRef} style={{ height: `${ITEM_HEIGHT * TOTAL_COUNT}px` }}>
    {data.map((item, index) => {
      return (
        <div 
          key={index} 
          data-index={index}
          style={{ 
            height: `${ITEM_HEIGHT}px`,
            position: 'absolute',
            top: `${index * ITEM_HEIGHT}px`,
            width: '100%',
          }} 
          ref={index >= intersectingStart && index <= intersectingEnd ? observerRef : null}
        >
          {item}
        </div>
      );
    })}
  </div>
</div>

以上代码中,我们使用useRef在可视区域内的节点中遍历检测是否进入了可视区域,利用useState记录可视区域的起始位置和结束位置。

示例二:使用缓存技术进一步优化性能

在示例一中,我们已经使用IntersectionObserver API来监听可视区域变化,但是节点的渲染仍然有性能瓶颈。这是因为在动态生成节点时,我们每次需要重新创建并渲染整个节点。

为了解决这个问题,可以使用React的缓存机制,存储可视区域内的节点。在下一次滚动时,我们仅需更新可视区域的样式即可,从而大幅减少渲染节点的时间和性能开销。以下是示例代码:

const ITEM_CACHE = new Map(); // 缓存可视区域内的节点
const CACHE_SIZE = 50; // 缓存的节点数

function getRenderList(start, end) {
  const renderList = [];

  for (let i = start; i <= end; i++) {
    if (ITEM_CACHE.has(i)) {
      renderList.push(ITEM_CACHE.get(i));
    } else {
      const item = (
        <div 
          key={i} 
          data-index={i}
          style={{ 
            height: `${ITEM_HEIGHT}px`,
            position: 'absolute',
            top: `${i * ITEM_HEIGHT}px`,
            width: '100%',
          }} 
          ref={start <= i && i <= end ? observerRef : null}
        >
          {data[i]}
        </div>
      );

      renderList.push(item);

      ITEM_CACHE.set(i, item);
      if (ITEM_CACHE.size > CACHE_SIZE) {
        ITEM_CACHE.delete(ITEM_CACHE.keys().next().value);
      }
    }
  }

  return renderList;
}

<div ref={listRef} style={{ 
  height: '500px',
  overflow: 'auto',
}} onScroll={handleScroll}>
  <div style={{ height: `${ITEM_HEIGHT * TOTAL_COUNT}px` }}>
    {getRenderList(start, end)}
  </div>
</div>

以上代码中,我们使用Map缓存已经渲染过的可视区域内的节点。如果缓存超过了CACHE_SIZE就清除缓存中最早的节点。在每次渲染节点时,先从缓存中查找是否已经渲染过,避免重复创建渲染。如果缓存中没有找到可重用的节点,再创建新的节点并存储在缓存中。这样在滚动过程中只需要更新可视区域内已经存在的节点,而无需重新创建和渲染整个节点,从而大幅提升了性能表现。

以上就是利用React实现虚拟列表的完整攻略,并给出了两个进一步的示例说明。希望能对你有所帮助!

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:利用React实现虚拟列表的示例代码 - Python技术站

(0)
上一篇 2023年6月10日
下一篇 2023年6月10日

相关文章

  • 老生常谈JavaScript获取CSS样式的方法(兼容各浏览器)

    获取CSS样式是JavaScript中常见的操作,但在不同的浏览器中会有些许差异。下文将介绍兼容各浏览器的JavaScript获取CSS样式的方法,其中包括元素内联样式、内部样式和外部样式。 获取元素内联样式 元素内联样式指的是在HTML标签内用style属性设置的样式。获取该样式的方式如下: var element = document.getElemen…

    css 2023年6月10日
    00
  • 前端面试必备之CSS3的新特性

    我来讲解一下。 前端面试必备之CSS3的新特性 1. CSS3的属性选择器 在CSS3中,新增了一些属性选择器,让选择元素更加灵活方便。下面介绍两种常用的属性选择器: 1.1 属性存在选择器 语法:[attribute] 这个选择器可以匹配指定属性的元素。例如: input[type] 这段代码选择所有具有”type”属性的input元素。如果我们想匹配所有…

    css 2023年6月9日
    00
  • JS设置CSS样式的方式汇总

    关于“JS设置CSS样式的方式汇总”的完整攻略,以下是我的观点: 1. 通过style属性直接设置 在JavaScript代码中,您可以使用元素的style属性来直接更改CSS样式。这种方式的优点是简单快捷,缺点是不适用于更复杂的样式更改。示例代码如下: let element = document.getElementById("myElemen…

    css 2023年6月9日
    00
  • CSS Flexbox的具体用法详解

    CSS Flexbox的具体用法详解 简介 Flexbox 是一种 CSS 布局方式,它提供了一种更加灵活的方式来布局网页元素。 Flexbox 通过弹性盒子的方式,让元素具有伸缩性和对齐性,使得网页可以更加快速地适应不同的屏幕尺寸和设备。 CSS Flexbox 布局模型 Flexbox 布局模型是基于弹性容器 (flex container) 和弹性子元…

    css 2023年6月10日
    00
  • CSS的相邻兄弟选择器用法简单讲解

    当我们需要选中文档中一个元素旁边的另一个元素时,就可以使用CSS的相邻兄弟选择器。相邻兄弟选择器用“+”符号表示,是紧跟在某个元素后的第一个指定元素。下面来详细讲解一下相邻兄弟选择器的用法。 语法规则: element1 + element2 element1: 要选择元素的前一个兄弟元素。 +: 选择前一个兄弟元素紧随的下一个兄弟元素。 element2:…

    css 2023年6月9日
    00
  • JavaScript+Canvas实现文字粒子流特效

    下面是“JavaScript+Canvas实现文字粒子流特效”的完整攻略。 1.了解Canvas 在使用Canvas前,要先了解一下Canvas的基本知识。Canvas是HTML5新提出的一项二维图形技术,在网页中实现动画效果、游戏绘画、数据图形绘制等功能,通常使用JavaScript与之配合。 2.准备文本素材 首先需要准备一张用于生成粒子效果的文本素材,…

    css 2023年6月10日
    00
  • create-react-app开发常用配置教程

    下面是针对“create-react-app开发常用配置教程”的完整攻略: 1、create-react-app是什么? create-react-app 是 React 官方推出的一个用于创建 React 项目的 CLI 工具,它可以帮助我们快速构建一个完整的 React 项目,无须进行配置,只需一条命令即可生成整个应用架构。create-react-ap…

    css 2023年6月9日
    00
  • jQuery过滤选择器经典应用

    接下来我将详细讲解“jQuery过滤选择器经典应用”的完整攻略。 一、什么是jQuery过滤选择器 jQuery过滤选择器是指根据一定的条件对HTML元素进行过滤筛选,最终获得需要的元素。过滤选择器一般用于从匹配元素集合中筛选出符合特定条件的元素,可以帮助我们更快地定位需要的元素,提高代码效率。 常用的jQuery过滤选择器有以下几种: 过滤选择器 说明 :…

    css 2023年6月10日
    00
合作推广
合作推广
分享本页
返回顶部