第
ahooksuseVirtualList封装虚拟滚动列表
目录简介实现原理具体实现思考总结
简介
提供虚拟化列表能力的Hook,用于解决展示海量数据渲染时首屏渲染缓慢和滚动卡顿问题。
详情可见官网,文章源代码可以点击这里。
实现原理
其实现原理监听外部容器的scroll事件以及其size发生变化的时候,触发计算逻辑算出内部容器的高度和marginTop值。
具体实现
其监听滚动逻辑如下:
//当外部容器的size发生变化的时候,触发计算逻辑
useEffect(()={
if(!size.width||!size.height){
return;
//重新计算逻辑
calculateRange();
},[size.width,size.height,list]);
//监听外部容器的scroll事件
useEventListener(
scroll,
e={
//如果是直接跳转,则不需要重新计算
if(scrollTriggerByScrollToFunc.current){
scrollTriggerByScrollToFunc.current=false;
return;
e.preventDefault();
//计算
calculateRange();
//外部容器
target:containerTarget,
);
其中calculateRange非常重要,它基本实现了虚拟滚动的主流程逻辑,其主要做了以下的事情:
获取到整个内部容器的高度totalHeight。根据外部容器的scrollTop算出已经滚过多少项,值为offset。根据外部容器高度以及当前的开始索引,获取到外部容器能承载的个数visibleCount。并根据overscan(视区上、下额外展示的DOM节点数量)计算出开始索引(start)和(end)。根据开始索引获取到其距离最开始的距离(offsetTop)。最后根据offsetTop和totalHeight设置内部容器的高度和marginTop值。
变量很多,可以结合下图,会比较清晰理解:
代码如下:
//计算范围,由哪个开始,哪个结束
constcalculateRange=()={
//获取外部和内部容器
//外部容器
constcontainer=getTargetElement(containerTarget);
//内部容器
constwrapper=getTargetElement(wrapperTarget);
if(containerwrapper){
const{
//滚动距离顶部的距离。设置或获取位于对象最顶端和窗口中可见内容的最顶端之间的距离
scrollTop,
//内容可视区域的高度
clientHeight,
}=container;
//根据外部容器的scrollTop算出已经“滚过”多少项
constoffset=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-ignore
wrapper.style.height=totalHeight-offsetTop+px;
//margintop为上方高度
//@ts-ignore
wrapper.style.marginTop=offsetTop+px;
//设置最后显示的List
setTargetList(
list.slice(start,end).map((ele,index)=({