antd5 虚拟列表原理(rc-virtual-list)
github:https://github.com/react-component/virtual-list
rc-virtual-list 版本 3.11.4(2024-02-01)
版本:virtual-list-3.11.4

Development
npm install
npm start
open http://localhost:8000/

List 组件接收 Props
| Prop | Description | Type | Default |
|---|---|---|---|
| children | Render props of item | (item, index, props) => ReactElement | - |
| component | Customize List dom element | string | Component | div |
| data | Data list | Array | - |
| disabled | Disable scroll check. Usually used on animation control | boolean | false |
| height | List height | number | - |
| itemHeight | Item minium height | number | - |
| itemKey | Match key with item | string | - |
| styles | style | { horizontalScrollBar?: React.CSSProperties; horizontalScrollBarThumb?: React.CSSProperties; verticalScrollBar?: React.CSSProperties; verticalScrollBarThumb?: React.CSSProperties; } | - |
组件解析
import ResizeObserver from "rc-resize-observer";const onHolderResize: ResizeObserverProps["onResize"] = (sizeInfo) => {console.log("sizeInfo", sizeInfo);setSize({width: sizeInfo.width || sizeInfo.offsetWidth,height: sizeInfo.height || sizeInfo.offsetHeight,});
};// 用于监听dom节点resize时返回dom节点信息
<ResizeObserver onResize={onHolderResize}></ResizeObserver>;
打印的 sizeInfo
{height: 200,//可视区高度offsetHeight: 200,offsetWidth: 606,width: 606,//可视区宽度
}
//component: Component = 'div',
// Component默认是div标签 ,className为rc-virtual-list-holder, 是虚拟列表的可视化区域
<ComponentclassName={`${prefixCls}-holder`}style={componentStyle}ref={componentRef}onScroll={onFallbackScroll}onMouseEnter={delayHideScrollBar}>
componentStyle 计算,是一个 styles 对象 React.CSSProperties
const ScrollStyle: React.CSSProperties = {overflowY: "auto",overflowAnchor: "none",
};// useVirtual: 是否虚拟列表(属性virtual为true 并且height和itemHeight有值)
const useVirtual = !!(virtual !== false && height && itemHeight);let componentStyle: React.CSSProperties = null;
if (height) {componentStyle = {[fullHeight ? "height" : "maxHeight"]: height,...ScrollStyle,};if (useVirtual) {componentStyle.overflowY = "hidden";if (scrollWidth) {componentStyle.overflowX = "hidden";}if (scrollMoving) {componentStyle.pointerEvents = "none";}}
}
overflow-anchor CSS 属性提供一种退出浏览器滚动锚定行为的方法,该行为会调整滚动位置以最大程度地减少内容偏移。
默认情况下,在任何支持滚动锚定行为的浏览器中都将其启用。因此,仅当你在文档或文档的一部分中遇到滚动锚定问题并且需要关闭行为时,才通常需要更改此属性的值。
内容组件
import Filler from ‘./Filler’;
<FillerprefixCls={prefixCls}height={scrollHeight}offsetX={offsetLeft}offsetY={fillerOffset}scrollWidth={scrollWidth}onInnerResize={collectHeight}ref={fillerInnerRef}innerProps={innerProps}rtl={isRTL}extra={extraContent}
>{listChildren}
</Filler>
Filler 组件
<div style={outerStyle}><ResizeObserveronResize={({ offsetHeight }) => {if (offsetHeight && onInnerResize) {onInnerResize();}}}><divstyle={innerStyle}className={classNames({[`${prefixCls}-holder-inner`]: prefixCls,})}ref={ref}{...innerProps}>{children}{extra}</div></ResizeObserver>
</div>
demo 查看渲染内容

outStyle 计算:
let outerStyle: React.CSSProperties = {};if (offsetY !== undefined) {// Not set `width` since this will break `sticky: right`outerStyle = {height,position: "relative",overflow: "hidden",};
}
innerStyle 计算
let innerStyle: React.CSSProperties = {display: "flex",flexDirection: "column",
};
if (offsetY !== undefined) {innerStyle = {...innerStyle,transform: `translateY(${offsetY}px)`,[rtl ? "marginRight" : "marginLeft"]: -offsetX,position: "absolute",left: 0,right: 0,top: 0,};
}
可以看到最终渲染的元素,有下面几个容器组成:
列表容器:rc-virtual-list
列表内容容器:rc-virtual-list-holder
要点:
Component 组件,默认 div:固定高度,超出部分隐藏,最终也是通过控制该容器的滚动高度来达到元素滚动的目的
div(outStyle):高度为所有列表内容都渲染出来的高度,这里是为了撑开父元素,实现父元素的滚动
渲染列表容器:rc-virtual-list-holder-inner
单个列表内容:item
listChildren
const listChildren = useChildren(mergedData, //列表数据start, //渲染第一个元素的索引end, //渲染最后一个元素的索引scrollWidth,setInstanceRef, //获取元素children,sharedConfig
);
useChildren 主要是进行 list 列表的渲染,而在渲染列表时,又用 Item 组件进行了一层包裹.
export default function useChildren<T>(list: T[],startIndex: number,endIndex: number,scrollWidth: number,setNodeRef: (item: T, element: HTMLElement) => void,renderFunc: RenderFunc<T>,{ getKey }: SharedConfig<T>
) {return list.slice(startIndex, endIndex + 1).map((item, index) => {const eleIndex = startIndex + index;const node = renderFunc(item, eleIndex, {style: {width: scrollWidth,},}) as React.ReactElement;const key = getKey(item);return (<Item key={key} setRef={(ele) => setNodeRef(item, ele)}>{node}</Item>);});
}
Item 组件
用 Item 组件包裹了外部传入的列表元素的 JSXElement
export interface ItemProps {children: React.ReactElement;setRef: (element: HTMLElement) => void;
}export function Item({ children, setRef }: ItemProps) {const refFunc = React.useCallback((node) => {setRef(node);}, []);return React.cloneElement(children, {ref: refFunc,});
}
经过这么一层包装,当通过 ref 获取子节点时,将会调用 refFunc -> setRef -> setInstanceRef。这也是为什么当元素高度可变时需要用 React.forwardRef 进行列表元素的包裹
滚动条组件
<ScrollBarref={verticalScrollBarRef}prefixCls={prefixCls}scrollOffset={offsetTop}scrollRange={scrollHeight}rtl={isRTL}onScroll={onScrollBar} //滚动事件onStartMove={onScrollbarStartMove} //开始滚动事件onStopMove={onScrollbarStopMove} //滚动结束事件spinSize={verticalScrollBarSpinSize}containerSize={size.height}style={styles?.verticalScrollBar}thumbStyle={styles?.verticalScrollBarThumb}
/>
ScrollBar 渲染
<divref={scrollbarRef}className={classNames(scrollbarPrefixCls, {[`${scrollbarPrefixCls}-horizontal`]: horizontal,[`${scrollbarPrefixCls}-vertical`]: !horizontal,[`${scrollbarPrefixCls}-visible`]: visible,})}style={{ ...containerStyle, ...style }}onMouseDown={onContainerMouseDown}onMouseMove={delayHidden}
><divref={thumbRef}className={classNames(`${scrollbarPrefixCls}-thumb`, {[`${scrollbarPrefixCls}-thumb-moving`]: dragging,})}style={{ ...thumbStyle, ...propsThumbStyle }}onMouseDown={onThumbMouseDown}/>
</div>
通过滚动条组件滚动事件
//newScrollOffset 滚动的距离,horizontal是否水平滚动方向
function onScrollBar(newScrollOffset: number, horizontal?: boolean) {const newOffset = newScrollOffset;if (horizontal) {flushSync(() => {setOffsetLeft(newOffset);});triggerScroll();} else {syncScrollTop(newOffset);}
}
滚动条开始滚动事件和滚动结束事件
// 滚动开始事件
const onScrollbarStartMove = () => {console.log("----start-----");setScrollMoving(true);
};//滚动结束事件
const onScrollbarStopMove = () => {console.log("-----end");setScrollMoving(false);
};
注意点
- 如果子项存在动态高度或者高度不统一的情况,需要使用 React.forwardRef 转发 ref 给子 DOM 元素。
- 列表项之间不要存在上下间距( margin-top 、 margin-bottom )。
以上两点如果没有做到,调用组件的 scrollTo(scrollConfig) 方法进行滚动时都会导致滚动位置异常
相关文章:
antd5 虚拟列表原理(rc-virtual-list)
github:https://github.com/react-component/virtual-list rc-virtual-list 版本 3.11.4(2024-02-01) 版本:virtual-list-3.11.4 Development npm install npm start open http://localhost:8000/List 组件接收 Props PropDescriptionTypeDefaultchildrenRender …...
机器学习-04-分类算法-03KNN算法
总结 本系列是机器学习课程的系列课程,主要介绍机器学习中分类算法,本篇为分类算法与knn算法部分。 本门课程的目标 完成一个特定行业的算法应用全过程: 懂业务会选择合适的算法数据处理算法训练算法调优算法融合 算法评估持续调优工程化…...
Learn OpenGL 08 颜色+基础光照+材质+光照贴图
我们在现实生活中看到某一物体的颜色并不是这个物体真正拥有的颜色,而是它所反射的(Reflected)颜色。物体的颜色为物体从一个光源反射各个颜色分量的大小。 创建光照场景 首先需要创建一个光源,因为我们以及有一个立方体数据,我们只需要进行…...
springboot多模块下swaggar界面出现异常(Knife4j文档请求异常)或者界面不报错但是没有显示任何信息
继上一篇博文,我们解决了多模块下扫描不到子模块的原因,建议先看上一个博客了解项目结构: springboot 多模块启动报错Field XXX required a bean of type XXX that could not be found. 接下来我们来解决swaggar异常的原因,我们成功启动项目…...
【系统架构设计师】系统工程与信息系统基础 01
系统架构设计师 - 系列文章目录 01 系统工程与信息系统基础 文章目录 系列文章目录 前言 一、系统工程 ★ 二、信息系统生命周期 ★ 信息系统建设原则 三、信息系统开发方法 ★★ 四、信息系统的分类 ★★★ 1.业务处理系统【TPS】 2.管理信息系统【MIS】 3.决策支持系统…...
python自动化之(django)(2)
1、创建应用 python manage.py startapp apitest 这里还是从上节开始也就是命令行在所谓的autotest目录下来输入 然后可以清楚的看到 多了一个文件夹 2、创建视图 在views中加入test函数(所建应用下) from django.http import HttpResponse def tes…...
C语言 内存函数
目录 前言 一、memcpy()函数 二、memmove()函数 三、memset函数 四、memcmp()函数 总结 前言 在C语言中内存是我们用来存储数据的地址,今天我们来讲一下C语言中常用的内存函数。 一、memcpy()函数 memcpy()函数与我们之前讲的strcpy()函数类似,只…...
145 Linux 网络编程1 ,协议,C/S B/S ,OSI 7层模型,TCP/IP 4层模型,
一 协议的概念 从应用的角度出发,协议可理解为“规则”,是数据传输和数据的解释的规则。 典型协议 传输层 常见协议有TCP/UDP协议。 应用层 常见的协议有HTTP协议,FTP协议。 网络层 常见协议有IP协议、ICMP协议、IGMP协议。 网络接口层 常…...
【Java】List, Set, Queue, Map 区别?
目录 List, Set, Queue, Map 区别? Collection和Collections List ArrayList 和 Array区别? ArrayList与LinkedList区别? ArrayList 能添加null吗? ArrayList 插入和删除时间复杂度? LinkedList 插入和删除时间复杂度&…...
打卡学习kubernetes——了解k8s基本概念
目录 1 Container 2 Pod 3 Node 4 Namespace 5 Service 6 Label 7 Annotations 8 Volume 1 Container Container(容器)是一种便携式、轻量级的操作系统级虚拟化技术。它使用namespace隔离不同的软件运行环境,并通过镜像自包含软件的运行环境,从而…...
特殊内齿轮加工的另一种选择
内齿轮加工普遍采用插齿或拉削,但对于一些特殊齿廓的内齿轮来说,插齿可能会有一定的困难,或者成本较高。在这种情况下,线切割加工不失为一种不错的选择。那么什么样的零件需要选择这种加工方式呢?一起来看看࿱…...
Visual Studio配置libtorch(cuda安装一步到位)
Visual Studio配置libtorch visual Studio安装cuDNN安装CUDAToolkit安装libtorch下载Visual Studio配置libtorch(cuda版本配置) visual Studio安装 visual Studio点击安装 具体的安装和配置过程这里就不进行细讲了,可以参考我这篇博客Visual Studio配置OpenCV(保姆…...
【工具】一键生成动态歌词字幕
那眼神如此熟悉 让人着迷无力抗拒 一次又一次相遇 在眼前却遥不可及 命运总爱淘气 将一切都藏匿 曾有你的回忆 无痕迹 若不是心心相吸 又怎么会一步一步靠近 🎵 董真《思如雪》 下载LRC歌词 https://www.musicenc.com/article/50287.htmlhttp…...
Linux/Ubuntu/Debian从控制台启动程序隐藏终端窗口
如果你想从终端运行应用程序但隐藏终端窗口. 你可以这样做: 在后台运行: 你只需在命令末尾添加一个与号 (&) 即可在后台运行它。 例如: your_command &将 your_command 替换为你要运行的命令。 这将在后台启动该命令,…...
Android中的设计模式---单例模式
1.什么是单例模式? 单例模式是一种创建型设计模式。它保证一个类只有一个实例,并且这个单例类提供一个函数接口让其他类获取到这个唯一的实例。 2.什么情况下会用到单例? ①频繁访问数据库或文件的对象; ②工具类对象; ③创建对象时耗时过多或耗费资源过多,但又经常用…...
【NLP笔记】文本分词、清洗和标准化
文章目录 文本分词中文分词英文分词代码示例 文本清洗和标准化 文本分词 参考文章:一文看懂NLP里的分词(中英文分词区别3 大难点3 种典型方法); 文本分词处理NLP的基础,先通过对文本内容进行分词、文本与处理(无用标…...
2024 年系统架构设计师(全套资料)
2024年5月系统架构设计师最新第2版教材对应的全套视频教程、历年真题及解析、章节分类真题及解析、论文写作及范文、教材、讲义、模拟题、答题卡等资料 1、2023年11月最新第2版本教材对应全套教程视频,2022年、2021年、2020年、2018年、2016年五套基础知识精讲视频、…...
springboot蛋糕订购小程序的设计与实现
摘 要 相比于以前的传统手工管理方式,智能化的管理方式可以大幅降低商家的运营人员成本,实现了蛋糕订购的标准化、制度化、程序化的管理,有效地防止了蛋糕订购的随意管理,提高了信息的处理速度和精确度,能够及时、准确…...
MongoDB——linux中yum命令安装及配置
一、创建mongodb-org-3.4.repo文件 vi /etc/yum.repos.d/mongodb-org-3.4.repo 将下面内容添加到创建的文件中 [mongodb-org-3.4] nameMongoDB Repository baseurlhttps://repo.mongodb.org/yum/amazon/2013.03/mongodb-org/3.4/x86_64/ gpgcheck1 enabled1 gpgkeyhttps://www…...
序列化笔记
第三章 序列化 3.1 概述 Java 提供了一种对象序列化的机制。用一个字节序列可以表示一个对象,该字节序列包含该对象的数据、对象的类型和对象中存储的属性等信息。字节序列写出到文件之后,相当于文件中持久保存了一个对象的信息。 反之,该…...
除了ulimit -c unlimited:深入理解Linux core dump机制与高级配置指南
深入Linux核心转储:从基础配置到生产环境实战指南当服务器上的关键应用突然崩溃时,系统管理员最需要的就是一份完整的"事故现场记录"。Linux的core dump机制正是为此而生,它能保存程序崩溃时的内存状态、寄存器值和调用堆栈&#x…...
癫痫手术精准定位:基于脑电信号昼夜节律与多生物标志物的机器学习分析框架
1. 项目概述:当机器学习遇见脑电信号,如何让癫痫手术更精准?作为一名长期耕耘在生物医学信号处理与机器学习交叉领域的工程师,我常常思考如何将算法模型从实验室的“玩具”变成临床医生手中可靠的“手术刀”。癫痫,这个…...
Python开发者首次使用Taotoken接入大模型API的完整步骤指南
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 Python开发者首次使用Taotoken接入大模型API的完整步骤指南 对于Python开发者而言,接入大模型API进行应用开发已成为一…...
论文创新点像挤牙膏?导师强推这几个AI论文平台
想写论文又快又好,关键是用对 AI 工具、走对流程——资深教授普遍推荐:千笔AI(中文全流程首选) 豆包学术版(轻量高效) DeepSeek 学术版(理工 / 长文本) Grammarly Academicÿ…...
户外实用|艾迪欧 R6000 测评 —— 户外 / 自驾 / 露营的通讯好搭档
户外出行,通讯工具的核心是稳定、清晰、耐用、续航久、功能全。艾迪欧 R6000 作为一款兼顾专业与户外的 DMR 对讲机,全频段覆盖、双模通讯、自定义功能、长续航,完美适配自驾、露营、登山、越野等户外场景,是户外爱好者的靠谱通讯…...
Burp Suite深度解析:从流量抓包到业务逻辑漏洞挖掘
1. 这不是“学个插件”——Burp Suite 是渗透测试的呼吸系统 很多人第一次听说 Burp Suite,是在某篇“三步拿下登录框”的速成教程里:装好Java、拖进浏览器代理、点几下Repeater就弹出密码明文。结果真去测一个中型SaaS后台,不到十分钟就卡在…...
告别手动复制!用这个自定义编辑器脚本一键备份/克隆Unity Terrain Data
告别手动复制!用这个自定义编辑器脚本一键备份/克隆Unity Terrain Data在Unity关卡设计和技术美术的工作流中,地形数据的灵活复用往往意味着反复的手动操作——导出高度图、备份材质参数、复制植被分布,每个环节都可能成为效率瓶颈。想象这样…...
PS5 NOR Modifier深度解析:如何通过Windows工具修复PS5硬件故障与实现光驱版转数字版
PS5 NOR Modifier深度解析:如何通过Windows工具修复PS5硬件故障与实现光驱版转数字版 【免费下载链接】PS5NorModifier The PS5 Nor Modifier is an easy to use Windows based application to rewrite your PS5 NOR file. This can be useful if your NOR is corru…...
构建智能音乐档案:SoundCloud Downloader 的技术架构与实现哲学
构建智能音乐档案:SoundCloud Downloader 的技术架构与实现哲学 【免费下载链接】scdl Soundcloud Music Downloader 项目地址: https://gitcode.com/gh_mirrors/sc/scdl 在流媒体音乐主导的时代,音乐爱好者面临着一种矛盾:我们享受着…...
基于EMA与轻量级机器学习的Wi-Fi链路质量预测实战
1. 项目概述与核心价值在工业自动化、仓储物流和智能制造等场景里,无线网络的稳定性正变得前所未有的重要。想象一下,一个自动导引运输车(AGV)正在执行物料搬运任务,或者一个机械臂正在与中央控制系统进行实时数据同步…...
