本文是深入浅出的ahooks源码系列文章的第十八篇。本系列已整理成文档地址。觉得还不错,给个star支持一下,谢谢。Hook提供虚拟化列表能力,用于解决海量数据渲染时首屏渲染慢、滚动卡顿的问题。详情可查看官网,文章源码可点击此处。实现原理实现原理是监听外部容器的scroll事件,当其大小发生变化时,触发计算逻辑计算内部容器的height和marginTop值。其监听和滚动逻辑的具体实现如下://当外部容器大小发生变化时,触发计算逻辑useEffect(()=>{if(!size?.width||!size?.height){return;}//重新计算逻辑calculateRange();},[size?.width,size?.height,list]);//监听外部容器的滚动事件useEventListener('scroll',e=>{//如果是直接跳转,不需要重新计算/外部容器目标:containerTarget,},);其中,calculateRange非常重要。基本实现了虚拟滚动的主要流程逻辑。它主要做了以下几件事:获取整个内部容器的totalHeight。根据外层容器的scrollTop计算已经“滚动”了多少item,值为offset。根据外部容器的高度和当前起始索引,得到外部容器可承载个数的visibleCount。并根据过扫描(视口上方和下方显示的额外DOM节点数)计算起始索引(start)和(end)。根据起始索引,得到距起始点的距离(offsetTop)。最后根据offsetTop和totalHeight设置内层容器的height和marginTop值。变量有很多,结合下图可以清楚的理解:代码如下://计算范围,从哪开始,从哪结束constcalculateRange=()=>{//获取外容器和内容器//外层容器constcontainer=getTargetElement(containerTarget);//内部容器constwrapper=getTargetElement(wrapperTarget);if(container&&wrapper){const{//从顶部滚动的距离。设置或获取对象顶部与窗口中可见内容顶部的距离scrollTop,//内容可见区域的高度clientHeight,}=container;//根据外部容器的scrollTop计算已经“滚动”了多少itemconstoffset=getOffset(scrollTop);//DOM可见区域个数constvisibleCount=getVisibleCount(clientHeight,offset);//开始下标conststart=Math.max(0,offset-overscan);//结束下标constend=Math.min(list.length,offset+visibleCount+overscan);//获取顶部高度constoffsetTop=getDistanceTop(start);//设置内层容器的高度,总高度-顶部高度//@ts-ignorewrapper.style.height=totalHeight-offsetTop+'px';//margintop是顶部高度//@ts-ignorewrapper.style.marginTop=offsetTop+'px';//设置最后显示的ListsetTargetList(list.slice(start,end).map((ele,index)=>({data:ele,index:index+start,})),);}};其他都是该函数的辅助函数,包括:根据外容器的高度和内部的每一项计算可见区域的个数://根据外容器的高度和内部的每一项计算可见区域的个数内部的每个项目constgetVisibleCount=(containerHeight:number,fromIndex:number)=>;{//知道每一行的高度-number类型,然后根据容器计算if(isNumber(itemHeightRef.current)){returnMath.ceil(containerHeight/itemHeightRef.current);}//动态指定每个元素的高度letsum=0;让endIndex=0;for(leti=fromIndex;i
