下面,我们来系统的梳理关于 React 虚拟化列表:react-window 的基本知识点:


一、虚拟化列表核心概念

1.1 什么是虚拟化列表?

虚拟化列表(也称为窗口化)是一种只渲染当前可见区域列表项的技术,而不是一次性渲染整个列表。通过仅渲染用户视口内的元素,可以大幅提升大型列表的性能。

1.2 为什么需要虚拟化列表?

  • 性能提升:减少 DOM 节点数量,提高渲染效率
  • 内存优化:避免加载所有数据到内存中
  • 流畅滚动:确保大型列表的平滑滚动体验
  • 快速响应:提升应用整体响应速度

1.3 虚拟化原理

┌───────────────────────┐
│   可视区域 (Viewport)  │
│ ┌───────────────────┐ │
│ │   可见项 1        │ │
│ │   可见项 2        │ │
│ │   可见项 3        │ │
│ └───────────────────┘ │
│                       │
│  不可见项(不渲染)    │
└───────────────────────┘

二、react-window 核心组件

2.1 固定高度列表 (FixedSizeList)

适用于所有项目高度相同的列表

import { FixedSizeList as List } from 'react-window';const Row = ({ index, style }) => (<div style={style}>第 {index} 行</div>
);const MyList = () => (<Listheight={600}   // 列表可视高度width={300}    // 列表宽度itemCount={1000} // 总项目数itemSize={50}  // 每个项目高度>{Row}</List>
);

2.2 可变高度列表 (VariableSizeList)

适用于项目高度不同的列表

import { VariableSizeList as List } from 'react-window';const rowHeights = new Array(1000).fill(true).map(() => 25 + Math.round(Math.random() * 50));const Row = ({ index, style }) => (<div style={style}>第 {index} 行,高度: {rowHeights[index]}px</div>
);const MyList = () => (<Listheight={600}width={300}itemCount={1000}itemSize={index => rowHeights[index]} // 动态高度函数estimatedItemSize={50} // 预估高度,用于滚动条计算>{Row}</List>
);

2.3 固定尺寸网格 (FixedSizeGrid)

适用于固定尺寸的网格布局

import { FixedSizeGrid as Grid } from 'react-window';const Cell = ({ columnIndex, rowIndex, style }) => (<div style={style}>行 {rowIndex}, 列 {columnIndex}</div>
);const MyGrid = () => (<Gridheight={600}width={900}columnCount={10}    // 总列数rowCount={1000}    // 总行数columnWidth={90}   // 列宽rowHeight={50}     // 行高>{Cell}</Grid>
);

2.4 可变尺寸网格 (VariableSizeGrid)

适用于动态尺寸的网格布局

import { VariableSizeGrid as Grid } from 'react-window';const columnWidths = new Array(10).fill(true).map(() => 75 + Math.round(Math.random() * 50));
const rowHeights = new Array(1000).fill(true).map(() => 25 + Math.round(Math.random() * 50));const Cell = ({ columnIndex, rowIndex, style }) => (<div style={style}>{rowIndex},{columnIndex}</div>
);const MyGrid = () => (<Gridheight={600}width={900}columnCount={10}rowCount={1000}columnWidth={index => columnWidths[index]}rowHeight={index => rowHeights[index]}estimatedColumnWidth={100}estimatedRowHeight={50}>{Cell}</Grid>
);

三、核心特性与高级用法

3.1 滚动控制

import { useRef } from 'react';function MyList() {const listRef = useRef();const scrollToRow200 = () => {listRef.current.scrollToItem(200);};const scrollToCenter = () => {listRef.current.scrollToItem(300, "center");};return (<><button onClick={scrollToRow200}>滚动到第200项</button><button onClick={scrollToCenter}>滚动到第300项(居中)</button><FixedSizeListref={listRef}height={600}width={300}itemCount={1000}itemSize={50}>{Row}</FixedSizeList></>);
}

3.2 无限滚动加载

import { useState, useCallback } from 'react';const PAGE_SIZE = 20;function InfiniteList() {const [items, setItems] = useState(Array(100).fill().map((_, i) => `项目 ${i + 1}`));const [isLoading, setIsLoading] = useState(false);const loadMoreItems = useCallback(() => {if (isLoading) return;setIsLoading(true);// 模拟API请求setTimeout(() => {const newItems = Array(PAGE_SIZE).fill().map((_, i) => `项目 ${items.length + i + 1}`);setItems(prev => [...prev, ...newItems]);setIsLoading(false);}, 1000);}, [isLoading, items.length]);const Row = useCallback(({ index, style }) => {if (index >= items.length) {return (<div style={style}><button onClick={loadMoreItems}>加载更多</button></div>);}return (<div style={style}>{items[index]}</div>);}, [items, loadMoreItems]);return (<FixedSizeListheight={600}width={300}itemCount={items.length + 1} // 额外一项用于"加载更多"itemSize={50}>{Row}</FixedSizeList>);
}

3.3 动态尺寸调整

function DynamicSizeList() {const listRef = useRef();const [rowHeights, setRowHeights] = useState({});// 更新项目高度const setRowHeight = (index, height) => {listRef.current.resetAfterIndex(0);setRowHeights(prev => ({...prev,[index]: height}));};const Row = ({ index, style }) => (<DynamicRow index={index} style={style} setHeight={setRowHeight}/>);const getItemSize = (index) => rowHeights[index] || 50;return (<VariableSizeListref={listRef}height={600}width={300}itemCount={1000}itemSize={getItemSize}estimatedItemSize={50}>{Row}</VariableSizeList>);
}// 动态高度行组件
function DynamicRow({ index, style, setHeight }) {const rowRef = useRef();useEffect(() => {if (rowRef.current) {setHeight(index, rowRef.current.clientHeight);}}, [index, setHeight]);return (<div ref={rowRef} style={style}>{/* 动态内容 */}项目 {index}<div style={{ height: 30 + (index % 10) * 5 }}>动态高度内容</div></div>);
}

四、性能优化技巧

4.1 使用 PureComponent 或 React.memo

// 使用 React.memo 避免不必要的行渲染
const Row = React.memo(({ index, style }) => {return (<div style={style}>项目 {index}</div>);
});// 使用 shouldComponentUpdate
class RowClass extends React.PureComponent {render() {const { index, style } = this.props;return <div style={style}>项目 {index}</div>;}
}

4.2 避免内联函数

// 错误 ❌:每次渲染创建新函数
<FixedSizeList>{({ index, style }) => <div style={style}>项目 {index}</div>}
</FixedSizeList>// 正确 ✅:缓存行组件
const Row = React.useCallback(({ index, style }) => (<div style={style}>项目 {index}</div>
), []);<FixedSizeList>{Row}
</FixedSizeList>

4.3 合理使用 estimatedItemSize

对于可变尺寸列表,提供准确的预估尺寸可以优化滚动条精度:

<VariableSizeListestimatedItemSize={100} // 预估项目尺寸// ...其他属性
/>

五、最佳实践与常见场景

5.1 大型数据列表

import { FixedSizeList } from 'react-window';const BigList = ({ data }) => {const Row = ({ index, style }) => (<div style={style}><h3>{data[index].name}</h3><p>{data[index].description}</p></div>);return (<FixedSizeListheight={600}width={800}itemCount={data.length}itemSize={120}>{Row}</FixedSizeList>);
};

5.2 表格渲染

import { FixedSizeGrid } from 'react-window';const DataTable = ({ columns, data }) => {const Cell = ({ columnIndex, rowIndex, style }) => (<div style={{...style,padding: '8px',borderBottom: '1px solid #eee',display: 'flex',alignItems: 'center'}}>{data[rowIndex][columns[columnIndex].key]}</div>);return (<FixedSizeGridheight={600}width={1000}columnCount={columns.length}rowCount={data.length}columnWidth={150}rowHeight={50}>{Cell}</FixedSizeGrid>);
};

5.3 图片画廊

import { VariableSizeGrid } from 'react-window';const ImageGallery = ({ images }) => {// 计算列宽和行高const columnWidth = 150;const rowHeight = 150;const columnCount = Math.floor(800 / columnWidth);const rowCount = Math.ceil(images.length / columnCount);const Cell = ({ columnIndex, rowIndex, style }) => {const index = rowIndex * columnCount + columnIndex;if (index >= images.length) return null;return (<div style={style}><img src={images[index].thumbnail} alt={images[index].title}style={{ width: '100%', height: '100%', objectFit: 'cover' }}/></div>);};return (<VariableSizeGridheight={600}width={800}columnCount={columnCount}rowCount={rowCount}columnWidth={() => columnWidth}rowHeight={() => rowHeight}>{Cell}</VariableSizeGrid>);
};

六、常见问题与解决方案

6.1 滚动位置重置问题

问题:列表数据更新后滚动位置重置
解决方案:使用 key 属性保持列表稳定

<FixedSizeListkey={stableListId} // 当stableListId不变时保持滚动位置// ...其他属性
/>

6.2 滚动条跳动问题

问题:可变尺寸列表滚动条位置不准确
解决方案

  1. 提供准确的 estimatedItemSize
  2. 使用 resetAfterIndex 方法更新尺寸缓存
const listRef = useRef();// 当项目尺寸变化时
useEffect(() => {listRef.current.resetAfterIndex(0);
}, [sizeData]);

6.3 内存泄漏问题

问题:卸载组件后仍有事件监听
解决方案:使用 ref 清理函数

useEffect(() => {const listElement = listRef.current;return () => {listElement?.removeEventListener('scroll', handleScroll);};
}, []);

七、与其他库对比

特性react-windowreact-virtualized@tanstack/react-virtual
包大小~5kB~15kB~3kB
性能优秀优秀优秀
维护性活跃维护维护较少活跃维护
功能丰富度核心功能功能全面功能全面
学习曲线简单中等中等
TypeScript支持优秀良好优秀
文档质量良好优秀优秀

八、案例:社交媒体动态流

import { VariableSizeList } from 'react-window';
import AutoSizer from 'react-virtualized-auto-sizer';const FeedList = ({ posts }) => {// 测量每篇文章高度并缓存const sizeMap = useRef({});const listRef = useRef();const setSize = useCallback((index, size) => {sizeMap.current = { ...sizeMap.current, [index]: size };listRef.current.resetAfterIndex(index);}, []);const getSize = (index) => sizeMap.current[index] || 100;const Row = ({ index, style }) => (<FeedItem post={posts[index]} style={style} index={index}setSize={setSize}/>);return (<AutoSizer>{({ height, width }) => (<VariableSizeListref={listRef}height={height}width={width}itemCount={posts.length}itemSize={getSize}estimatedItemSize={300}>{Row}</VariableSizeList>)}</AutoSizer>);
};const FeedItem = React.memo(({ post, style, index, setSize }) => {const rowRef = useRef();useEffect(() => {if (rowRef.current) {setSize(index, rowRef.current.clientHeight);}}, [index, setSize, post]);return (<div ref={rowRef} style={style}><div className="post-header"><img src={post.user.avatar} alt={post.user.name} /><h3>{post.user.name}</h3></div><p>{post.content}</p>{post.image && (<img src={post.image} alt="Post" style={{ maxWidth: '100%' }}/>)}<div className="post-actions"><button>点赞</button><button>评论</button><button>分享</button></div></div>);
});

九、总结

9.1 react-window 核心优势

  • 轻量高效:极小的包体积,出色的性能
  • 简单易用:直观的 API 设计
  • 灵活性强:支持固定和可变尺寸
  • 良好兼容:完美支持 React 18 新特性
  • 功能完备:覆盖列表、网格等常见场景

9.2 实践总结

  1. 优先选择固定尺寸:当项目尺寸相同时使用 FixedSizeList
  2. 合理预估尺寸:对可变尺寸列表提供准确预估
  3. 避免内联函数:缓存行渲染函数
  4. 使用尺寸缓存:动态测量项目尺寸并缓存
  5. 配合 AutoSizer:自动适应容器尺寸
  6. 虚拟化一切可滚动内容:表格、网格、画廊等

9.3 性能优化矩阵

场景推荐组件优化技巧
等高等宽列表FixedSizeList使用 React.memo 优化行组件
等高不等宽列表FixedSizeGrid精确计算列宽
动态高度列表VariableSizeList使用尺寸测量和缓存
大型表格FixedSizeGrid分区域渲染
瀑布流布局VariableSizeGrid动态计算行列尺寸

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

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

相关文章

2025AI颠覆认知!解锁智能新纪元

清晨的城市还裹着薄雾时&#xff0c;通勤族的手机已经自动规划好最优路线——避开施工路段、实时更新交通状况&#xff0c;连早餐店排队人数都能精准预测。这不是科幻电影里的片段&#xff0c;而是2025年AI深度融入生活的寻常场景。当数字化与智能化浪潮席卷而来&#xff0c;我…

实用Shell高级视频课程

实用Shell高级视频课程 Shell三剑客sed我网盘给你分享了「实用Shell高级视频课程」&#xff0c;点击链接或复制整段内容&#xff0c;打开「APP」即可获取。/bc3b37jg8i:/链接&#xff1a;http://t.cn/A6swtV7u提取码&#xff1a;ePV4 ​​​

hive-日期拆分为多行

hive-日期拆分为多行 代码 SELECT begin_date,date_add(begin_date, tmp.pos),end_date,d_days,tmp.pos,tmp.val FROM (SELECT begin_date,end_date,DATEDIFF(end_date, begin_date) AS d_daysFROM (SELECT 2025-08-01 AS begin_date,2025-08-10 AS end_date) a) b LA…

全志MPP学习(1)-全志MPP概念理清

文章目录1、全志MPP1.1、MPP-Framework1.2、MPP-Middleware1.3、MPP-Framework和MPP-Middleware之间的关系2、总结1、全志MPP 全志MPP&#xff08;Media Process Platform&#xff09;媒体处理软件平台&#xff0c;分为 mpp-middleware 和 mpp-framework 两部分。 mpp-middlew…

Linux操作系统启动项相关研究与总结

Linux操作系统启动项相关研究与总结 一、Linux Systemd 服务创建与管理研究 1. Systemd 服务基础 1.1 Systemd 服务文件位置 1.2 服务文件基本结构 2. 创建自定义 Systemd 服务 2.1 基本服务文件示例 2.2 服务文件详细配置选项 [Unit] 部分常用指令: [Service] 部分常用指令:…

Go map 的性能革命:深入解析从链表到 Swiss Table 的优化之路

你好&#xff0c;Gopher&#xff01;map 作为 Go 语言中最核心、最常用的数据结构之一&#xff0c;其性能直接影响着我们程序的效率。在 Go 1.24 版本中&#xff0c;map的底层实现迎来了一次意义深远的变革&#xff0c;从沿用多年的“哈希桶链表”结构&#xff0c;悄然升级为了…

化工厂安全升级:分布式光纤传感的 “实时监测 + 精准预警” 方案

分布式光纤传感技术凭借长距离连续监测、抗电磁干扰、耐腐蚀、高灵敏度、实时响应等特性&#xff0c;非常适配化工领域中化学原料及化学制品工厂的复杂环境&#xff0c;如高温、高压、腐蚀性介质、强电磁干扰等&#xff0c;在安全生产、设备维护、风险预警等方面发挥着关键作用…

供应链需求预测项目如何设定合理的KPI、准确率指标(十四)

本篇文章适合希望优化供应链管理的读者&#xff0c;尤其是对KPI的选择与应用有兴趣的人。文章的亮点在于揭示了不当KPI使用可能导致的风险&#xff0c;如狭隘的关注、协作减少和与业务目标不一致等&#xff0c;同时提供了如何选择合适KPI的最佳实践。 本文整合自文章&#xff…

【线性代数】线性方程组与矩阵——(1)线性方程组与矩阵初步

上一节&#xff1a;无 总目录&#xff1a;【线性代数】目录 文章目录1. 线性方程组2. 矩阵的引入2.1. 矩阵的定义2.2. 常见的矩阵2.3. 线性方程组中常用的矩阵2.4. 线性变换与矩阵3. 矩阵的运算3.1. 矩阵的加法3.2. 矩阵的数乘3.3. 矩阵的乘法3.4. 矩阵的转置3.5. 方阵的行列式…

【工具变量】地市人力资本水平数据集(2003-2023年)

数据简介&#xff1a;普通本专科在校学生数作为人力资本的代理变量&#xff0c;能够直观反映区域教育投入与人才储备规模。通过与户籍人口数比值计算&#xff0c;可消除人口基数差异&#xff0c;实现跨区域人力资本水平的横向比较。 人力资本水平是个体价值创造能力与国家竞争…

轻量化阅读应用实践:21MB无广告电子书阅读器测评

还在为广告满天飞的阅读软件烦恼吗&#xff1f;今天阿灿给大家推荐一款纯净好用的阅读神器&#xff0c;安读&#xff01;这款app只有21MB大小&#xff0c;但功能真的很贴心。最棒的是完全没广告&#xff0c;让你能静下心来好好看书。支持各种电子书格式&#xff0c;打开就能读&…

嵌入式硬件篇---OpenMV存储

OpenMV存储部分OpenMV 开发板的存储部分可以简单理解为 “不同用途的存储器”&#xff0c;就像我们的电脑有硬盘&#xff08;存文件&#xff09;、内存&#xff08;临时运行程序&#xff09;一样&#xff0c;OpenMV 也有几个不同的存储区域&#xff0c;各自分工明确。下面用通俗…

QT第二讲-信号和槽

文章目录 ⚙️ 一、基本概念与规则 1. 信号(Signal) 2. 槽(Slot) 🔌 二、连接函数 connect() 详解 函数原型: 参数说明 类型 行为 场景 🧩 三、实际场景示例 场景1:按钮点击关闭窗口 场景2:实时验证输入框文本 ⚡️ 四、高级技巧 1. Lambda表达式作为槽 2. 处理信号…

如何用OpenAI SDK调用Ollama LLM

Ollama目前内置了OpenAI Chat Completions API 的兼容端点&#xff0c;用户可以用OpenAI SDK访问本地Ollama模型&#xff0c;这里示例整个访问过程。 假设Ollama已安装&#xff0c;过程参考 在mac m1基于ollama运行deepseek r1_mac m1 ollama-CSDN博客 1 下载OpenAI SDK和模型…

如何解决用阿里云效流水线持续集成部署Nuxt静态应用时流程卡住,进行不下去的问题

我有一个用Nuxt搭建的前端应用&#xff0c;部署时是用npm run generate命令生成静态页&#xff0c;然后上传到服务器上的指定目录来完成部署。之前是写了一个shell脚本&#xff0c;用rsync命令实现的上传&#xff0c;个人用起来倒也比较方便&#xff0c;但是因为涉及到服务器登…

Java中Lambda表达式的常见用法和解析:从入门到实战

引言在Java 8发布之前&#xff0c;Java语言一直以面向对象为核心&#xff0c;代码风格相对严谨但有时显得冗长。随着函数式编程思想的兴起&#xff0c;Java 8引入了Lambda表达式这一革命性特性&#xff0c;极大地简化了代码编写&#xff0c;提升了开发效率。Lambda表达式不仅让…

【Python 高频 API 速学 ③】

一、为什么先学这 5 个&#xff1f; • 它们覆盖了「切 → 洗 → 拼 → 换 → 排版」整条链路。 • 任意一段文本处理脚本&#xff0c;80 % 的操作都能用这 5 个方法写完。二、五虎上将一览方法作用典型场景易踩的坑split(sepNone)按分隔符切成列表日志拆字段、CSV 解析连续分隔…

前端百分比展示导致后端 BigDecimal 转换异常的排查与解决

在开发一个订单预算系统时&#xff0c;我们需要在前端动态计算「利润率差额」&#xff0c;格式为百分比&#xff08;带 % 符号&#xff09;保留4位小数&#xff0c;但实际传给后端时必须是纯数字&#xff08;浮点数&#xff09;&#xff0c;以便后端正常以 BigDecimal 类型接收…

论文学习21:Pyramid Scene Parsing Network

代码来源 GitHub - hszhao/PSPNet: Pyramid Scene Parsing Network, CVPR2017. 模块作用 对于不受限制的开放词汇和多样化场景&#xff0c;场景解析极具挑战性。本文结合金字塔池化模块和提出的金字塔场景解析网络&#xff08;PSPNet&#xff09;&#xff0c;利用基于不同区…

从手工编码到自动化:APP开发的效率革命

摘要**熬夜敲代码、反复调试改 Bug&#xff0c;项目进度却依旧缓慢&#xff0c;这是无数 APP 开发者在手工编码时代的真实写照。更让人崩溃的是&#xff0c;即便投入大量时间精力&#xff0c;最终交付的 APP 还可能存在各种问题。难道 APP 开发注定如此艰辛&#xff1f;不&…