发布时间:2025-06-24 18:01:34 作者:北方职教升学中心 阅读量:047
它同时适用于 PC 端和移动端,通过虚拟化技术实现了延迟加载和无限滚动功能,尤其是非常适合需要高效渲染和加载大量数据的应用场景,如聊天记录、
首先从视觉感官上看,几乎是一瞬间图表就加载了出来。传统的渲染方式可能会面临性能问题,因为它们需要在页面上同时呈现大量 DOM 元素,导致页面加载缓慢、它们帮助避免不必要的计算和重新渲染。这种方法存在以下问题:
- 性能消耗大:频繁监听滚动事件会导致性能消耗增加,尤其是在大型数据集的情况下。而
react-virtualized-list
库的核心在于通过虚拟化技术优化渲染过程。此外,
react-virtualized-list
库还提供了场景适用的效果展示和示例代码。其主要原理包括以下几点:1. 可视区域监测:利用Intersection Observer API
在虚拟化列表的实现中,一个关键步骤是监测可视区域内的元素。
constvisibleRange =useMemo(()=>{constsortedVisibleItems =[...visibleItems].sort((a,b)=>a -b);constfirstVisible =sortedVisibleItems[0]||0;constlastVisible =sortedVisibleItems[sortedVisibleItems.length -1]||0;// 设置缓存区return[Math.max(0,firstVisible -BUFFER_SIZE),Math.min(listData.length -1,lastVisible +BUFFER_SIZE)];},[visibleItems,listData.length]);constrenderItems=()=>{returnlistData.length ?listData.map((item,index)=>{if(index >=visibleRange[0]&&index <=visibleRange[1]){return(<div className={itemClassName ||undefined}style={itemContainerStyle}ref={node=>handleRef(node,index)}key={index}data-index={index}><VirtualizedListItem item={listData[index]}isVisible={visibleItems.has(index)}refreshOnVisible={refreshOnVisible}fetchItemData={fetchItemData}itemLoader={itemLoader}>{renderItem}</VirtualizedListItem></div>);}returnnull;}):(emptyListMessage ?emptyListMessage :null);};
当元素进入视口时,我们加载它;当元素离开视口时,我们卸载它。
希望本文能对你有所帮助,有所借鉴!大家有什么疑问或者建议,欢迎在评论区一起讨论。
综上所述,Intersection Observer API
在处理大型数据集和复杂交互时,相比传统的 scroll
事件监听方式,提供了更高的性能和更灵活的解决方案。我们接着再来看看接口Network与数据对比!
为了清楚地展示优化前后页面加载速度的提升,我们可以将相关数据整理成一个表格形式,如下所示:
优化指标 | 优化前 | 优化后 | 加载速度提升 |
---|---|---|---|
总耗时 | 15000 毫秒(15秒) | 750 毫秒 | 提速了95% |
这个表格展示了优化措施的显著效果,从中可以看出,经过优化后,整体加载时间也从15000毫秒大幅减少至750毫秒,加载速度提高了95%。
// 定义一个 Intersection Observerconstobserver =newIntersectionObserver(entries=>{entries.forEach(entry=>{// 如果元素可见if(entry.isIntersecting){// 执行相应操作console.log(`${entry.target}is visible.`);}});});// 获取需要监测可视性的元素constelements =document.querySelectorAll('.target-element');// 监测每个元素elements.forEach(element=>{observer.observe(element);});
这里我封装了一个 React Hooks useIntersectionObserver
,提供了Intersection Observer API
的能力。
性能对比(🔥性能飙升 50%)
下面我们就来看下,传统滚动 Scroll 监听和 Intersection Observer API 的性能对比数据(假设在相同环境和数据集下测试):
方法 | 初始渲染时间 | 滚动性能 | 内存使用 |
---|---|---|---|
传统滚动监听 | 300ms | 低 | 高 |
Intersection Observer API | 150ms | 高 | 低 |
- 初始渲染时间:使用 Intersection Observer API 的初始渲染时间较短,因为只渲染可见区域。这部分代码的执行会阻塞主线程,尤其在滚动频繁的情况下可能导致性能问题,因为需要不断重新计算元素位置。
- 自定义渲染:提供灵活的 API,允许开发者自定义列表项的渲染方式。
为了解决这个问题,我们可以使用虚拟化列表来优化渲染过程。
- 滚动性能:传统滚动监听由于频繁的滚动事件触发和位置计算,滚动性能较低;Intersection Observer API 的滚动性能较高,因为它利用了浏览器的优化机制。
相比之下,Intersection Observer API 的性能更优,具有以下优点:
- 性能开销低:
Intersection Observer API
利用浏览器的内部优化机制,减少了不必要的计算和事件触发,从而提高了性能。 - 延迟加载:动态加载数据,避免一次性加载大量数据带来的性能问题。传统的方法是通过监听滚动事件并计算每个元素的位置来实现,然而这种方式效率较低。
项目成果展示(🔥渲染速度提升95%)
下面我们看下优化后的性能,展示实际改进的用户体验和加载时间。这使得在处理复杂布局和交互时更加高效。由于核心需求是列表项数据需要动态更新和自动刷新,所以用到了 react-virtualized-list 库。
- 支持 TS 和 JS:适用于 TypeScript 和 JavaScript 项目。页面元素动画等。这有助于保持页面的响应性和流畅性。
- 无限滚动:支持无限滚动,用户可以持续滚动查看更多内容。滚动卡顿等问题。
除此之外,通过使用
useMemo
计算当前可见的列表项范围 (visibleRange
),以及设置一个缓冲区 (BUFFER_SIZE
);使用useMemo
和useCallback
用于性能优化的 Hook。这个过程相当曲折,具体业务需求细节后面我会详细写一篇文章,这里先介绍一下react-virtualized-list
库的特性、这种功能常见于滚动加载下页数据。如果你也在处理大数据集的渲染问题,不妨试试这个库。 - 计算复杂度高:需要手动计算每个列表项与视口的交叉情况,逻辑复杂且容易出错。
相比之下,Intersection Observer API 更高效。
- 多元素监测:
Intersection Observer API
允许同时监测多个元素的交叉状态,而不需要为每个元素都绑定事件监听器。 - 视口内刷新:支持自动刷新视口内的内容,确保数据的实时性。
参考资料
- Intersection Observer API
- react-virtualized-list
- 详解 Intersection Observer API ( 交叉观察器 )
性能测试代码分析
以下是一个示例,展示了如何使用 console.time 和 console.timeEnd 来测量性能:
// 测量传统滚动监听的性能console.time('Scroll');window.addEventListener('scroll',()=>{// 模拟计算每个元素的位置constelements =document.querySelectorAll('.target-element');elements.forEach(element=>{constrect =element.getBoundingClientRect();if(rect.top >=0&&rect.bottom <=window.innerHeight){// 模拟渲染逻辑}});});console.timeEnd('Scroll');// 测量 Intersection Observer API 的性能console.time('IntersectionObserver');constobserver =newIntersectionObserver(entries=>{entries.forEach(entry=>{if(entry.isIntersecting){// 模拟渲染逻辑}});});constelements =document.querySelectorAll('.target-element');elements.forEach(element=>observer.observe(element));console.timeEnd('IntersectionObserver');
注意:传统滚动监听方法还会涉及大量计算,这里仅简单测量了监听性能的统计部分。以下是一个示例,展示了如何自定义列表项的样式和内容:
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;
此外,
react-virtualized-list
还提供了其他的用法场景和相关 API,详情请见使用文档。这种技术在处理大量数据时尤为重要,因为它显著减少了 DOM 节点的数量,从而提高了性能。通过虚拟化,可以在用户滚动列表时动态加载和卸载元素,保持界面流畅。引言:在处理大规模数据集渲染时,前端性能常常面临巨大的挑战。本文将探讨
react-virtualized-list
库如何通过虚拟化技术和 Intersection Observer,实现前端渲染性能飙升 50%的突破,页面渲染速度提升 95%!🔥🚀
背景
最近,公司监控系统出现了加载卡顿和白屏问题,需要一个能够处理大规模数据渲染的方案。当用户滚动到可视区域时,新的元素被动态加载,而离开可视区域的元素则被卸载,从而减少页面的内存占用。
importuseIntersectionObserver from'./useIntersectionObserver';const[visibleItems,setVisibleItems]=useState<Set<number>>(newSet());consthandleVisibilityChange =useCallback((isVisible:boolean,entry:IntersectionObserverEntry)=>{constindex =parseInt(entry.target.getAttribute('data-index')!,10);setVisibleItems(prev=>{constnewVisibleItems =newSet(prev);if(isVisible){newVisibleItems.add(index);}else{newVisibleItems.delete(index);}returnnewVisibleItems;});},[]);const{observe,unobserve }=useIntersectionObserver(containerRef.current,handleVisibilityChange,null,observerOptions);
3. 动态加载和卸载:保持内存使用最小化
最后,虚拟化列表还可以通过动态加载和卸载元素来保持内存使用最小化。它可以检测元素是否可见,并在元素进入或退出视窗时触发回调函数,从而实现需要的功能。这对于大数据集下,后端无法一次性返回数据非常有利!
自定义渲染
react-virtualized-list
还提供了自定义渲染功能,开发者可以根据具体需求定制列表项的渲染方式。传统的滚动监听方式通过监听
scroll
事件,在每次滚动时计算每个目标元素的位置,并判断其是否在视窗内。以下是一个示例,展示了如何结合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
在虚拟化方面做的处理:
我们来看看真实的 DOM 情况!
react-virtualized-list
简介react-virtualized-list 是一个专门用于显示大型数据集的高性能 React 组件库。
Intersection Observer
会异步执行回调函数,不会阻塞主线程。需要花费大量时间和精力来优化和调试这些计算逻辑。实现原理(🔥核心重点,一定要了解)
在构建大型 Web 应用时,经常会遇到需要展示大量数据的情况,比如电子商务平台的产品列表等。
Intersection Observer API
可以应用于多种场景,如懒加载、安装
可以通过 npm 或 yarn 轻松安装 react-virtualized-list:
npminstallreact-virtualized-list# 或者yarnaddreact-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 调用,延迟 1 秒加载新数据 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;}
通过 onLoadMore
和 hasMore
属性实现无限滚动,在用户滚动到列表底部时自动加载更多数据。大家有什么疑问或者建议,欢迎在评论区一起讨论!
什么是虚拟化?
虚拟化技术,顾名思义,是一种通过仅渲染当前用户可见的数据项,而不是整个数据集,来优化性能的技术。相比之下,传统的 scroll
事件监听方式由于密集触发,可能会导致较大的性能问题。统计结果显示:页面加载速度提高了 95%,用户体验得到了明显改善。适用场景、
核心特性 🔥🔥
- 高性能:仅渲染当前视口内的元素,显著减少 DOM 节点数量。
// 获取需要监测可视性的元素constelements =document.querySelectorAll('.target-element');// 监听滚动事件window.addEventListener('scroll',()=>{// 计算每个元素的位置elements.forEach(element=>{constrect =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 来更高效地监测元素的可见性变化。这样就可以保持页面上始终只有视口内的内容被渲染,从而提高页面的性能和响应速度。
性能总结
在性能方面,传统实现方法通常需要通过监听滚动(scroll)事件来计算元素位置。广告展示与统计、
2. 仅渲染可见区域:优化性能
虚拟化列表的另一个关键优化是仅渲染可见区域内的元素,而不是渲染整个列表。
总结
通过使用
react-virtualized-list
库,监控系统项目前端渲染性能得到了显著提升。希望对你有所帮助、这些应用场景通常需要高效地处理元素与视口之间的交互。
进阶用法
动态加载数据
为了进一步提高性能,可以使用动态加载技术,只在需要时加载数据。无限滚动、