性能飙升50%,react-virtualized-list如何优化大数据集滚动渲染

news2024/11/26 5:52:40

在处理大规模数据集渲染时,前端性能常常面临巨大的挑战。本文将探讨 react-virtualized-list 库如何通过虚拟化技术和 Intersection Observer API,实现前端渲染性能飙升 50% 的突破!除此之外,我们一同探究下该库还支持哪些新的特性和适用场景。

如果你正在寻找解决大数据集渲染瓶颈的方法,或是希望提升前端应用的响应速度,这篇文章将为你带来全新的启发与实用的解决方案。

什么是虚拟化?

虚拟化技术,顾名思义,是一种通过仅渲染当前用户可见的数据项,而不是整个数据集,来优化性能的技术。这种技术在处理大量数据时尤为重要,因为它显著减少了 DOM 节点的数量,从而提高了性能。通过虚拟化,可以在用户滚动列表时动态加载和卸载元素,保持界面流畅。

在这里插入图片描述

react-virtualized-list 简介

react-virtualized-list 是一个专门用于显示大型数据集的高性能 React 组件库。它同时适用于 PC 端移动端,通过虚拟化技术实现了延迟加载和无限滚动功能,尤其是非常适合需要高效渲染和加载大量数据的应用场景,如聊天记录、商品列表等。

此外,react-virtualized-list 库还提供了场景适用的效果展示和示例代码。

核心特性

  1. 高性能:仅渲染当前视口内的元素,显著减少 DOM 节点数量。
  2. 延迟加载:动态加载数据,避免一次性加载大量数据带来的性能问题。
  3. 无限滚动:支持无限滚动,用户可以持续滚动查看更多内容。
  4. 自定义渲染:提供灵活的 API,允许开发者自定义列表项的渲染方式。
  5. 视口内刷新:支持自动刷新视口内的内容,确保数据的实时性。

安装和基本用法

安装

可以通过 npm 或 yarn 轻松安装 react-virtualized-list:

npm install react-virtualized-list
# 或者
yarn add react-virtualized-list

基本用法

下面是一个简单的示例,展示了如何使用 react-virtualized-list 创建一个无限滚动的虚拟化列表:

import React, { useState, useEffect } from 'react';
import VirtualizedList from 'react-virtualized-list';
import './style/common.css';

const InfiniteScrollList = () => {
  const [items, setItems] = useState([]);
  const [hasMore, setHasMore] = useState(true);

  const loadMoreItems = () => {
    // 模拟 API 调用
    setTimeout(() => {
      const newItems = Array.from({ length: 20 }, (_, index) => ({
        id: items.length + index,
        text: `Item ${items.length + index}`
      }));
      setItems(prevItems => [...prevItems, ...newItems]);
      setHasMore(newItems.length > 0);
    }, 1000);
  };

  useEffect(() => {
    loadMoreItems();
  }, []);

  const renderItem = (item) => <div>{item.text}</div>;

  return (
      <div className='content'>
        <VirtualizedList
          listData={items}
          renderItem={renderItem}
          containerHeight='450px'
          itemClassName='item-class'
          onLoadMore={loadMoreItems}
          hasMore={hasMore}
          loader={<div>Loading...</div>}
          endMessage={<div>No more items</div>}
        />
      </div>
  );
};

export default InfiniteScrollList;
/* ./style/common.css  */
.content {
    width: 350px;
    padding: 16px;
    border: 1px solid red;
    margin-top: 10vh;
}
.item-class {
    height: 50px;
    border: 1px solid blue;
    margin: 0px 0 10px;
    padding: 10px;
    background-color: #f0f0f0;
}

通过 onLoadMorehasMore 属性实现无限滚动,在用户滚动到列表底部时自动加载更多数据。这种功能常见于滚动加载下页数据。

进阶用法

动态加载数据

为了进一步提高性能,可以使用动态加载技术,只在需要时加载数据。以下是一个示例,展示了如何结合 react-virtualized-list 和动态数据加载:

import React, { useState, useEffect } from 'react';
import VirtualizedList from 'react-virtualized-list';
import './style/common.css';

const fetchProductData = async (product) => {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve({ description: `Description for ${product.name}`, imageUrl: `https://via.placeholder.com/150?text=Product+${product.id}` });
    }, 500);
  });
};

const fetchProducts = async (page) => {
  return new Promise((resolve) => {
    setTimeout(() => {
      const products = Array.from({ length: 10 }, (_, i) => ({
        id: page * 10 + i,
        name: `Product ${page * 10 + i}`
      }));
      resolve(products);
    }, 500);
  });
};

const DynamicInfiniteList = () => {
  const [products, setProducts] = useState([]);
  const [hasMore, setHasMore] = useState(true);
  const [page, setPage] = useState(0);

  const loadMoreProducts = async () => {
    const newProducts = await fetchProducts(page);
    setProducts(prevProducts => [...prevProducts, ...newProducts]);
    setPage(prevPage => prevPage + 1);
    if (newProducts.length < 10) setHasMore(false);
  };

  useEffect(() => {
    loadMoreProducts();
  }, []);
   
  return (
      <div className='content'>
        <VirtualizedList
          listData={products}
          renderItem={(product, data) => (
            <div>
              <h2>{product.name}</h2>
              <p>{data ? data.description : 'Loading...'}</p>
              {data && <img src={data.imageUrl} alt={product.name} />}
            </div>
          )}
          itemClassName='item-class-dynamic'
          fetchItemData={fetchProductData}
          onLoadMore={loadMoreProducts}
          hasMore={hasMore}
          containerHeight='500px'
          loader='Loading more products...'
          endMessage='No more products'
        />
      </div>
  );
};

export default DynamicInfiniteList;
/* ./style/common.css  */
.content {
    width: 350px;
    padding: 16px;
    border: 1px solid red;
    margin-top: 10vh;
}
.item-class-dynamic {
    height: 300px;
    padding: 20px;
    border-bottom: 1px solid #eee;
}

注意:在上面代码中,我们使用 onLoadMore 模拟商品列表的滚动加载,并在 VirtualizedList 组件的 fetchItemData 实现了商品详情的动态加载。这对于大数据集下,后端无法一次性返回数据非常有利

自定义渲染

react-virtualized-list 还提供了自定义渲染功能,开发者可以根据具体需求定制列表项的渲染方式。以下是一个示例,展示了如何自定义列表项的样式和内容:

import React from 'react';
import VirtualizedList from 'react-virtualized-list';

const data = Array.from({ length: 1000 }).map((_, index) => ({
  title: `Item ${index}`,
  index: index,
  description: `This is the description for item ${index}.`
}));

const ListItem = ({ item, style }) => (
  <div style={{ ...style, padding: '10px', borderBottom: '1px solid #ccc' }}>
    <h3>{item.title}</h3>
    <p>{item.description}</p>
  </div>
);

const itemStyle = {
    height: '100px',
    border: '1px solid blue',
    margin: '0px 0 10px',
    padding: '10px',
    backgroundColor: '#f0f0f0'
};

const MyVirtualizedList = () => (
  <div style={{width: '350px', padding: '16px', border: '1px solid red'}}>
    <VirtualizedList
        listData={data}
        itemStyle={itemStyle}
        renderItem={({ index, style }) => <ListItem item={data[index]} style={style} />}
        containerHeight='80vh'
    />
  </div>
);

export default MyVirtualizedList;

通过自定义 ListItem 组件,我们可以轻松定制列表项的样式和内容,提升应用的个性化和用户体验。此外,react-virtualized-list 还提供了其他用法和相关 API,详情请见使用文档。

实现原理

在构建大型 Web 应用时,经常会遇到需要展示大量数据的情况,比如电子商务平台的产品列表等。传统的渲染方式可能会面临性能问题,因为它们需要在页面上同时呈现大量 DOM 元素,导致页面加载缓慢、滚动卡顿等问题。

为了解决这个问题,我们可以使用虚拟化列表来优化渲染过程。而 react-virtualized-list 库的核心在于通过虚拟化技术优化渲染过程。其主要原理包括以下几点:

在这里插入图片描述

1. 可视区域监测:利用Intersection Observer API

注意:react-virtualized-list 库在可视区域监测这块使用了另外一个库 react-visible-observer,它是一个基于 Intersection Observer API 的 React 组件库,用于监视元素何时进入视口,并在该元素可见时触发回调函数。这个组件特别适用于实现懒加载、动画触发等功能。

在虚拟化列表的实现中,一个关键步骤是监测可视区域内的元素。传统的方法是通过监听滚动事件并计算每个元素的位置来实现,然而这种方式效率较低。

// 获取需要监测可视性的元素
const elements = document.querySelectorAll('.target-element');

// 监听滚动事件
window.addEventListener('scroll', () => {
    // 计算每个元素的位置
    elements.forEach(element => {
        const rect = element.getBoundingClientRect();
        if (
            rect.top >= 0 &&
            rect.left >= 0 &&
            rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
            rect.right <= (window.innerWidth || document.documentElement.clientWidth)
        ) {
            // 元素在可视区域内
            // 执行相应操作
            console.log(`${element} is visible.`);
        }
    });
});

相比之下,我们可以利用现代浏览器提供的 Intersection Observer API 来更高效地监测元素的可见性变化。

// 定义一个 Intersection Observer
const observer = new IntersectionObserver(entries => {
    entries.forEach(entry => {
        // 如果元素可见
        if (entry.isIntersecting) {
            // 执行相应操作
            console.log(`${entry.target} is visible.`);
        }
    });
});

// 获取需要监测可视性的元素
const elements = document.querySelectorAll('.target-element');

// 监测每个元素
elements.forEach(element => {
    observer.observe(element);
});

2. 仅渲染可见区域:优化性能

虚拟化列表的另一个关键优化是仅渲染可见区域内的元素,而不是渲染整个列表。这样做可以大大减少渲染所需的时间和资源,提高页面的性能表现。

const Child = ({ data }) => {
    const [content, setContent] = useState(null);
    return <div>{content ? content : 'Loading data...'}</div>;
};

3. 动态加载和卸载:保持内存使用最小化

最后,虚拟化列表还可以通过动态加载和卸载元素来保持内存使用最小化。当用户滚动到可视区域时,新的元素被动态加载,而离开可视区域的元素则被卸载,从而减少页面的内存占用。

我们可以利用前面提到的 Intersection Observer API 来实现这一功能。当元素进入视口时,我们加载它;当元素离开视口时,我们卸载它。这样就可以保持页面上始终只有视口内的内容被渲染,从而提高页面的性能和响应速度。

性能对比:传统 Scroll vs Intersection Observer API

以下是传统滚动监听和 Intersection Observer API 的性能对比数据(假设在相同环境和数据集下测试):

方法初始渲染时间滚动性能内存使用
传统滚动监听300ms
Intersection Observer API150ms
  • 初始渲染时间:使用 Intersection Observer API 的初始渲染时间较短,因为只渲染可见区域。
  • 滚动性能:传统滚动监听由于频繁的滚动事件触发和位置计算,滚动性能较低;Intersection Observer API 的滚动性能较高,因为它利用了浏览器的优化机制。
  • 内存使用:Intersection Observer API 由于仅加载和渲染可见元素,内存使用更低。

性能测试代码分析

以下是一个示例,展示了如何使用 console.time 和 console.timeEnd 来测量性能:

// 测量传统滚动监听的性能
console.time('Scroll');
window.addEventListener('scroll', () => {
    // 模拟计算每个元素的位置
    const elements = document.querySelectorAll('.target-element');
    elements.forEach(element => {
        const rect = element.getBoundingClientRect();
        if (rect.top >= 0 && rect.bottom <= window.innerHeight) {
            // 模拟渲染逻辑
        }
    });
});
console.timeEnd('Scroll');

// 测量 Intersection Observer API 的性能
console.time('IntersectionObserver');
const observer = new IntersectionObserver(entries => {
    entries.forEach(entry => {
        if (entry.isIntersecting) {
            // 模拟渲染逻辑
        }
    });
});
const elements = document.querySelectorAll('.target-element');
elements.forEach(element => observer.observe(element));
console.timeEnd('IntersectionObserver');

注意:传统滚动监听还会涉及计算,但这里只是简单的监听性能统计。

传统的滚动监听方式通过监听 scroll 事件,在每次滚动时计算每个目标元素的位置,并判断其是否在视窗内。这部分代码的执行会阻塞主线程,尤其在滚动频繁的情况下可能导致性能问题,因为需要不断重新计算元素位置。

相比之下,Intersection Observer API 更高效。它可以检测元素是否可见,并在元素进入或退出视窗时触发回调函数,从而实现需要的功能。

性能总结

在性能方面,传统实现方法通常需要通过监听滚动(scroll)事件来计算元素位置。这种方法存在以下问题:

  • 性能消耗大:频繁监听滚动事件会导致性能消耗增加,尤其是在大型数据集的情况下。
  • 计算复杂度高:需要手动计算每个列表项与视口的交叉情况,逻辑复杂且容易出错。需要花费大量时间和精力来优化和调试这些计算逻辑。

相比之下,Intersection Observer API 的性能更优,具有以下优点:

  1. 性能开销低Intersection Observer API 利用浏览器的内部优化机制,减少了不必要的计算和事件触发,从而提高了性能。相比之下,传统的 scroll 事件监听方式由于密集触发,可能会导致较大的性能问题。
  2. 多元素监测Intersection Observer API 允许同时监测多个元素的交叉状态,而不需要为每个元素都绑定事件监听器。这使得在处理复杂布局和交互时更加高效。
  3. 异步执行:当元素进入或离开交叉状态时,Intersection Observer 会异步执行回调函数,不会阻塞主线程。这有助于保持页面的响应性和流畅性。
  4. 应用场景广泛Intersection Observer API 可以应用于多种场景,如懒加载、无限滚动、广告展示与统计、页面元素动画等。这些应用场景通常需要高效地处理元素与视口之间的交互。

综上所述,Intersection Observer API 在处理大型数据集和复杂交互时,相比传统的 scroll 事件监听方式,提供了更高的性能和更灵活的解决方案。

结论

通过本文的介绍,我们了解了虚拟化列表的工作原理和优势,以及如何使用 react-virtualized-list 库来优化渲染性能。

希望本文能对你有所帮助,有所借鉴!大家有什么疑问或者建议,欢迎在评论区一起讨论。

参考资料

  1. Intersection Observer API
  2. react-virtualized-list
  3. 详解 Intersection Observer API ( 交叉观察器 )

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1795561.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【嵌入式DIY实例】-OLED显示天气数据

OLED显示天气数据 文章目录 OLED显示天气数据1、硬件准备与接线2、天气数据获取准备3、代码实现在这个物联网项目中,本文将展示如何使用 ESP8266 NodeMCU (ESP-12E) Wi-Fi 开发板和 SSD1306 OLED 显示屏(12864 像素)制作一个简单的互联网气象站。 NodeMCU 从天气网站 openwe…

JavaWeb3 Ajax+Axios+Element+Nginx部署

Ajax 异步JS和XML 1.数据交换&#xff1a;给服务器发送请求&#xff0c;并获取服务器相应的数据 2.异步交互&#xff1a;在不重新加载整个页面的情况下&#xff0c;与服务器交换数据并更新部分网页 同步与异步 原生Ajax <!DOCTYPE html> <html> <body><…

kafka-消费者-消费异常处理(SpringBoot整合Kafka)

文章目录 1、消费异常处理1.1、application.yml配置1.2、注册异常处理器1.3、消费者使用异常处理器1.4、创建生产者发送消息1.5、创建SpringBoot启动类1.6、屏蔽 kafka debug 日志 logback.xml1.7、引入spring-kafka依赖1.8、消费者控制台&#xff1a;1.8.1、第一次启动SpringK…

【WP】猿人学_17_天杀的Http2.0

https://match.yuanrenxue.cn/match/17 抓包分析 居然对Fiddler有检测&#xff0c;不允许使用 那就使用浏览器抓包&#xff0c;好像没发现什么加密参数&#xff0c;然后重放也可以成功&#xff0c;时间长了也无需刷新页面&#xff0c;尝试Python复现。 Python复现 import …

在线标注流程

文章目录 在线标注流程标注方法 在线标注流程 登录地址&#xff1a;http://7a27c5e078f644a2a9b734603913c65e.login.bce.baidu.com 出现页面&#xff1a; 登录名&#xff1a; 三个中任意一个 密码&#xff1a;ZNSJ123a 登录之后叉掉。再打开这个网站&#xff1a;https://…

CAD 文件(DXF / DWG)转换为(DXF / PDF / PNG / SVG)

方法一Github 这个是ezdxf出品的&#xff0c;可以使用命令行的方式进行转换 ezdxf draw -o file.<png|svg|pdf> <file.dxf>也可以自己改动代码 examples/addons/drawing/pdf_export.py 但是直接运行会有误&#xff0c;以下是我改动后的代码&#xff1a; from ez…

ArrayList——简单洗牌算法

特殊语法介绍&#xff1a; List<List<E>> 该语法情况比较特殊&#xff0c;相当于一个“二维数组”存着一个个线性表的结构&#xff0c;如图&#xff1a; 该语法的灵活性强&#xff0c;可适用于多种类型和多种情况。接下来就使用该语法来实现一个简单的洗牌操作。…

【Linux取经路】网络套接字编程——初识篇

文章目录 一、端口号1.1 认识端口号1.2 端口号 VS 进程 PID 二、认识 TCP 协议三、认识 UDP四、网络字节序列五、socket 编程接口5.1 常用 API5.2 sockaddr 结构 六、结语 一、端口号 网络通信的本质是应用层软件进行数据的发送和接受&#xff0c;软件在启动之后&#xff0c;本…

快速自定义表单开发的优势介绍

进行高效率的办公是很多职场人的梦想。借助什么样的软件平台可以提质增效&#xff1f;低代码技术平台是当前较为流行的办公软件平台产品&#xff0c;具有灵活性、易操作、好维护等多个优势特点&#xff0c;操作人员只需要像搭积木似地操作&#xff0c;就可以搭建属于客户的个性…

[element-ui]el-form自定义校验-图片上传验证(手动触发部分验证方法)

背景&#xff1a; 在做导入文件功能的时候&#xff0c;需要校验表单&#xff0c;如图所示 店铺字段绑定在表单数据对象上&#xff0c;在点击确定的时候正常按照表单验证规则去校验&#xff0c;就不再赘述。 文件上传是个异步过程&#xff0c;属性值改变后不会去触发验证规则…

一.网络基础——OSI七层模型

一.OSI七层模型 OSI&#xff08;Open System Interconnection&#xff0c;开放系统互连&#xff09;七层网络模型被称为开放式系统互联参考模型&#xff0c;它是一个逻辑上的定义和规范; 它把网络从逻辑上分为了7层. 每一层都有相关、相对应的物理设备&#xff0c;比如路由器&…

[数据集][目标检测]焊接处缺陷检测数据集VOC+YOLO格式3400张8类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;3400 标注数量(xml文件个数)&#xff1a;3400 标注数量(txt文件个数)&#xff1a;3400 标注…

FFmpeg播放器的相关概念【1】

播放器框架 相关术语 •容器&#xff0f;文件&#xff08;Conainer/File&#xff09;&#xff1a;即特定格式的多媒体文件&#xff0c;比如mp4、flv、mkv等。 • 媒体流&#xff08;Stream&#xff09;&#xff1a;表示时间轴上的一段连续数据&#xff0c;如一段声音数据、一段…

6.6学习总结

一.算法练习(Codeforces Round 949 (Div. 2)和) B. Turtle and an Infinite Sequence 思路&#xff1a;对于数字而言&#xff0c;轮之后的结果是所有数的或。因此只需要求区间或就行了。(其实就是找区间左边界,二进制中的特殊位置,将后面的所有位都变成1,最后输出结果) 代码实…

关于计算机是如何工作的

计算机的发展历程 世界上的第一个计算机 冯诺依曼机构体系 1.存储器 (包括内存(存储空间小,访问速度快,成本高,掉电后数据丢失) 外存(硬盘,软盘,U盘,光盘)),存储空间小,访问速度慢,成本低,掉电后数据仍在 2.CPU(中央处理单元,计算机最核心的部分,用于算术运算和逻辑判断),…

【ARM Cache 及 MMU 系列文章 6.2 -- ARMv8/v9 Cache 内部数据读取方法详细介绍】

请阅读【ARM Cache 及 MMU/MPU 系列文章专栏导读】 及【嵌入式开发学习必备专栏】 文章目录 Direct access to internal memoryL1 cache encodingsL1 Cache Data 寄存器Cache 数据读取代码实现Direct access to internal memory 在ARMv8架构中,缓存(Cache)是用来加速数据访…

算法金 | 再见!!!KNN

大侠幸会&#xff0c;在下全网同名「算法金」 0 基础转 AI 上岸&#xff0c;多个算法赛 Top 「日更万日&#xff0c;让更多人享受智能乐趣」 KNN算法的工作原理简单直观&#xff0c;易于理解和实现&#xff0c;这使得它在各种应用场景中备受青睐。 我们将深入探讨KNN算法&…

带你了解消防安全与应急救援,2024北京消防展6月盛大开启

带你了解消防安全与应急救援&#xff0c;2024北京国际消防展6.26盛大开启 在日益关注安全问题的今天&#xff0c;消防安全与应急救援已经成为社会发展的重要一环。为了提高全民消防安全意识&#xff0c;推动应急救援技术的发展&#xff0c;2024年北京国际消防展将于6月26日盛大…

Docker自定义镜像实现(SpringBoot程序为例)

✅作者简介&#xff1a;大家好&#xff0c;我是 Meteors., 向往着更加简洁高效的代码写法与编程方式&#xff0c;持续分享Java技术内容。&#x1f34e;个人主页&#xff1a;Meteors.的博客&#x1f49e;当前专栏&#xff1a;知识备份✨特色专栏&#xff1a;知识分享&#x1f96…

【窗口函数的详细使用】

前言&#xff1a; &#x1f49e;&#x1f49e;大家好&#xff0c;我是书生♡&#xff0c;今天主要和大家分享一下可MySQL中的窗口函数的概念&#xff0c;语法以及常用的窗口函数,希望对大家有所帮助。感谢大家关注点赞。 &#x1f49e;&#x1f49e;前路漫漫&#xff0c;希望大…