当前位置: 首页 > news >正文

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

PropDescriptionTypeDefault
childrenRender props of item(item, index, props) => ReactElement-
componentCustomize List dom elementstring | Componentdiv
dataData listArray-
disabledDisable scroll check. Usually used on animation controlbooleanfalse
heightList heightnumber-
itemHeightItem minium heightnumber-
itemKeyMatch key with itemstring-
stylesstyle{ 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) 方法进行滚动时都会导致滚动位置异常
http://www.lryc.cn/news/319450.html

相关文章:

  • 机器学习-04-分类算法-03KNN算法
  • Learn OpenGL 08 颜色+基础光照+材质+光照贴图
  • springboot多模块下swaggar界面出现异常(Knife4j文档请求异常)或者界面不报错但是没有显示任何信息
  • 【系统架构设计师】系统工程与信息系统基础 01
  • python自动化之(django)(2)
  • C语言 内存函数
  • 145 Linux 网络编程1 ,协议,C/S B/S ,OSI 7层模型,TCP/IP 4层模型,
  • 【Java】List, Set, Queue, Map 区别?
  • 打卡学习kubernetes——了解k8s基本概念
  • 特殊内齿轮加工的另一种选择
  • Visual Studio配置libtorch(cuda安装一步到位)
  • 【工具】一键生成动态歌词字幕
  • Linux/Ubuntu/Debian从控制台启动程序隐藏终端窗口
  • Android中的设计模式---单例模式
  • 【NLP笔记】文本分词、清洗和标准化
  • 2024 年系统架构设计师(全套资料)
  • springboot蛋糕订购小程序的设计与实现
  • MongoDB——linux中yum命令安装及配置
  • 序列化笔记
  • ArkTs的资源Resource类型怎么转为string
  • Apache zookeeper kafka 开启SASL安全认证
  • Rust 的 inline 内联编译策略
  • 软考73-上午题-【面向对象技术2-UML】-UML中的图4
  • html编辑器
  • 利用“定时执行专家”软件的25种任务与12种触发器,提升IT系统管理自动化水平
  • 设计模式学习笔记 - 规范与重构 - 8.实践:程序出错返回啥?NULL、异常、错误吗、空对象?重构ID生成器,处理各函数的异常
  • 【Python使用】python高级进阶知识md总结第4篇:静态Web服务器-命令行启动动态绑定端口号,html 的介绍【附代码文档】
  • langchain+chatglm3+BGE+Faiss Linux环境安装依赖
  • Kubernetes kafka系列 | k8s部署kafka+zookeepe集群(可外部通信)
  • IDEA创建Sping项目只能勾选17和21,没有Java8?