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

在处理大规模数据集渲染时,前端性能常常面临巨大的挑战。本文将探讨 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'><VirtualizedListlistData={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'><VirtualizedListlistData={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'}}><VirtualizedListlistData={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.mzph.cn/diannao/21662.shtml

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

相关文章

【Git篇 二】idea中使用git合并分支(拉取分支)

idea中使用git合并分支 前言idea使用git合并分支1) 将主分支&#xff08;master&#xff09;更新到自己的分支&#xff08;dev&#xff09;① checkout到自己分支② 目标分支&#xff08;dev&#xff09;更新到当前分支&#xff08;dev_KC240524&#xff09;③ 当前分支出现“绿…

【Python】 Python网络请求库大比拼:urllib、urllib2、urllib3与requests

基本原理 在Python中&#xff0c;进行网络请求是常见的任务之一&#xff0c;无论是下载网页内容、获取API数据还是进行文件上传。Python社区提供了多种库来帮助开发者完成这些任务。其中&#xff0c;urllib、urllib2、urllib3和requests是最为流行的几个。了解它们之间的区别对…

促进设备缺陷闭环管理,引入智能巡检系统正当时

经过近些年的应用与发展&#xff0c;智能巡检系统的功能与可操作性已经非常成熟&#xff0c;在巡检工作整合管理、与其他系统调用对接、促进设备缺陷闭环管理方面的优秀表现&#xff0c;使其在安全管理工作中的发挥了超预期的工具价值。 一、巡检工作整合管理 设备巡检管理、安…

QT 编译Lua 动态库,使用Lua脚本混合编程

一,编译Lua动态库 1,下载lua源码 地址:Lua: downloadhttps://www.lua.org/download.html 2,配置 解压lua源码压缩包,里面有个src文件夹,里面的代码就是lua的源码

Paper Survey——3DGS-SLAM

之前博客对多个3DGS SLAM的工作进行了复现及代码解读 学习笔记之——3DGS-SLAM系列代码解读_gs slam-CSDN博客文章浏览阅读1.9k次&#xff0c;点赞15次&#xff0c;收藏45次。最近对一系列基于3D Gaussian Splatting&#xff08;3DGS&#xff09;SLAM的工作的源码进行了测试与…

广东肇庆mes系统服务商 盈致科技

广东肇庆MES系统服务商盈致科技为企业提供专业的MES系统解决方案&#xff0c;帮助企业实现生产过程的数字化管理和优化。盈致科技的服务包括但不限于以下方面&#xff1a;MES系统定制开发&#xff1a;盈致科技可以根据企业的实际需求定制开发MES系统&#xff0c;满足企业特定的…

《猎杀:对决》是适合什么样的人玩 Mac电脑怎么玩《猎杀:对决》

《猎杀&#xff1a;对决》是一款集合了生存、竞技和恐怖元素的多人在线游戏&#xff0c;自推出以来受到了广大玩家的热爱。本文将详细探讨《猎杀&#xff1a;对决》适合什么样的人玩以及Mac电脑怎么玩《猎杀&#xff1a;对决》。本文将一一解析&#xff0c;帮助你了解这款游戏是…

maven中央仓库手动下载到本地仓库

1.maven中央仓库网址 maven中央仓库 2.搜索需要的jar包 3.点击坐标mybatis坐标进入 4.winR输入cmd打开命令窗口 mvn dependency:get -DremoteRepositoriesurl -DgroupIdgroupId -DartifactIdartifactId -Dversionversion5.maven本地仓库

判断JavaScript对象是否为空,最佳方法与性能对比

引言&#xff1a;在日常开发中&#xff0c;你是否经常需要判断一个对象是否为空&#xff1f;你知道有哪些高效的方法可以做到这一点吗&#xff1f;今天&#xff0c;我们不仅要探讨这些方法&#xff0c;还要通过性能对比找出最优解。快来看看&#xff0c;你平常用的方法是最佳选…

Linux安装gpu驱动

安装rpm包 进入官网 &#xff0c;选择机器的配置和环境。 下载完rpm之后&#xff0c;运行下面的命令。 i) sudo rpm -i nvidia-driver-local-repo-rhel8-550.54.15-1.0-1.x86_64.rpm ii) sudo dnf clean all iii) sudo dnf -y module install nvidia-driver:latest-dkms 安…

2024/5/30

In recent weeks, Target and Aldi have broadcast price cuts on thousands of items, while Walmart unveiled a new private label lineup of quality “chef-inspired food” mostly in the $5-and-under range. The shift comes as U.S. consumers have been signaling th…

煤矿塌陷位移监测站-全天候监测

TH-WY1煤矿塌陷位移监测站&#xff0c;作为矿山安全监测系统的重要组成部分&#xff0c;其设计、构建和功能均针对煤矿塌陷等地质灾害的实时监测与预警。 煤矿塌陷位移监测站&#xff0c;简称位移监测站&#xff0c;是一种利用先进传感器和仪器设备&#xff0c;对煤矿及周边区…

QML信号连接到c++的槽函数(五)

文章目录 前言一、QML Signal and Handler Event System二、QML信号连接到c++的槽函数代码实例1. 创建一个QML 工程2. 用C++ 实现一个QML Types3. 代码实例4. 运行结果总结参考资料前言 本文主要介绍,如何将QML 中的信号连接到C++ 中的槽函数 软硬件环境: 硬件:PC 软件:wi…

以hive metastore报错举例,远程调试hadoop服务

项目场景&#xff1a; CDH集群CM切换hive元数据库报错&#xff1a; com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Could not create connection to database server.at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)at…

步进电机噪音大?抖动厉害?-TMC4361步进闭环方案轻松解决

步进电机的闭环控制&#xff0c;实现与伺服电机功能媲美及超越的特点。在电机控制过程中如果想要高效、更高精度以及绝对的可靠性&#xff0c;我们为您推荐在无传感器及将体积做到最小的更高性价比的驱动方案。 我们都知道&#xff0c;在步进电机无反馈时&#xff0c;电机的高…

【刷题(16)】子串

一 子串基础 二 和位K的子数组 1 题目 2 解题思路 前缀和哈希&#xff08;两数之和&#xff09; 假如存在区间[left,right]&#xff0c;使得在[left,right]这个区间的子数组的和为k。换句话说&#xff0c;就是前right项和减去前left-1项和等于k&#xff0c;即前left-1项和等…

【机器学习】——驱动智能制造的青春力量,优化生产、预见故障、提升质量

目录 一.优化生产流程 1.1 数据收集 1.2 数据预处理 1.3 模型训练 1.4 优化建议 1.5 示例代码 二.预测设备故障 2.1 数据收集 2.2 数据预处理 2.3 模型训练 2.4 故障预测 2.5 示例代码 三.提升产品质量 3.1 数据收集 3.2 数据预处理 3.3 模型训练 3.4 质量提升…

07C回调函数

C零碎语法 目录 文章目录 C零碎语法流程1.声明一个函数指针2.定义回调函数3.注册回调函数 应用4.事件回调5.同步回调函数6.异步回调函数 流程 1.声明一个函数指针 uint8_t (*p_CallbackFunc)(void);2.定义回调函数 重要的是要注意回调函数只是一个函数。 由于它的使用方式(…

leetCode.90. 子集 II

leetCode.90. 子集 II 题目思路 代码 class Solution { public:vector<vector<int>> res;vector<int> path;vector<vector<int>> subsetsWithDup(vector<int>& nums) {// 先排序&#xff0c;让有相同元素的都放到一起sort(nums.begi…

msconfig修改引导导致电脑无法进入系统 和 启动蓝屏问题

目录 解决方法遇到的问题开机蓝屏进入【指定UFEI固件设置】进入bios启动系统选择界面&#xff08;忘了拍照&#xff09; 解决方法 找到一个移动U盘&#xff08;最好大于10G&#xff09;格式化&#xff1b;找一个好的电脑安装WEPE软件&#xff08;wepe安装链接&#xff09;&…