1、虚拟列表 核心原理就是 比如有100条数据 如果全部渲染出来,那么dom结构里面对应有100个对应的dom,比如1000 10000的时候,就会开始影响页面性能,那么就要实现虚拟列表, dom结构里面永远只渲染10条 或者 页面可展示的条数,那么性能就会大大提升!
2、实现原理
function Home(props: Props) {
let lessonListRef = useRef(null);
useEffect(()=>{
lessonListRef.current();
//重新渲染组件
homeContainerRef.current.addEventListener('scroll',throttle(lessonListRef.current,16));
},[]);//依赖数组为空表示此effect回调只会执行一次
return (
<>
<div className="home-container" ref={homeContainerRef}>
<LessonList
lessons={props.lessons}
getLessons={props.getLessons}
ref={lessonListRef}
homeContainerRef={homeContainerRef}
/>
</div>
</>
)
}
-
2.1 先定义ref : lessonListRef传入给LessonList组件,外层容器homeContainerRef绑定scroll事件,那么容器滚动的时候,就会执行lessonListRef.current方法。
-
2.2
let [,forceUpdate] = React.useReducer(x=>x+1,0);只是为了定义一个强制刷新的函数forceUpdate 然后赋值给forwardedRef.current = forceUpdate; -
2.3 remSize 就是1rem代表多少px 比如37.5
-
2.4 itemSize 就是计算当前每个item高度是多少px 比如原来是650px的设计图高度 650 /75就是rem rem*remSize就是实际高度
-
2.5 screenHeight 显示内容的高度
-
2.6 scrollTop 父容器滚动的距离
-
2.7 start = Math.floor(scrollTop/itemSize); start索引就是展示的起始索引 因为卷去的高度/item高度 就是代表滚到第几个
-
2.8 end = start + Math.floor(screenHeight/itemSize); screenHeight/itemSize就是屏幕最多展示的个数 开始+展示个数 就是end
-
2.9 为了滚动时 前后都看到有数据 start做减法-2 end做加法 + 2, 前后分别都加2个数据看到
-
3.0 visibleList 就是展示的list, 通过start end动态截取原始数据展示出来即可
-
3.1 关键是每个item都是绝对定位通过 top改变他的位置 top就是index*itemSize
-
3.2因为一开始就调用监听scroll 调用forceUpdate 所以每次滚动都会修改start end值 改变数据, 并且改变每个item top的值
function LessonList(props: PropsWithChildren<Props>,forwardedRef:any) {
//只是为了让我们得到一个强行刷新函数组件的方法
let [,forceUpdate] = React.useReducer(x=>x+1,0);
useEffect(() => {
if (props.lessons.list.length === 0) {
props.getLessons();
}
forwardedRef.current = forceUpdate;
},[]);
let remSize = parseFloat(document.documentElement.style.fontSize);//750px=>75px 375px=>37.5px
const itemSize = (650/75)*remSize;//每个条目实际的高度
//window.innerHeight=屏幕的高度=100vh -实际际的头和尾的高度 750px header 100px tab=121px=221
const screenHeight = window.innerHeight - (221/75) * remSize;//显示内容的容器的高度
const homeContainer = props.homeContainerRef.current;//DOM元素
let start=0,end=0;
if(homeContainer){
const scrollTop = homeContainer.scrollTop;//父容器向上卷去的高度
start = Math.floor(scrollTop/itemSize);
end = start + Math.floor(screenHeight/itemSize);
start -=2,end+=2;//右闭右开的区间
start=start<0?0:start;//小于0取0
end=end>props.lessons.list.length?props.lessons.list.length:end;// 如果大于最后一个,取最大值
}
//可视条目的列表
const visibleList:VisibleLesson[] = props.lessons.list.map((item:Lesson,index:number)=>({
...item,index
})).slice(start,end);
const basicStyle:React.CSSProperties = {
position:'absolute',
top:0,
left:0,
width:'100%',
height:itemSize
}
return (
<section className="lesson-list">
<h2><MenuOutlined />全部课程</h2>
<div style={{position:'relative',width:'100%',height:`${props.lessons.list.length*itemSize}px`}}>
{
visibleList.map((lesson: VisibleLesson, index: number) => (
<Link
key={lesson.id}
to={{ pathname: `/detail/${lesson.id}`, state: lesson }}
style={{...basicStyle,top:`${lesson.index*itemSize}px`}}
>
<Card
hoverable={true}
style={{ width: '100%' }}
cover={<img alt={lesson.title} src={lesson.poster} />}
>
<Card.Meta
title={lesson.title}
description={`价格:${lesson.price}元`}
/>
</Card>
</Link>
))
}
</div>
{
props.lessons.hasMore ? (
<Button onClick={props.getLessons} loading={props.lessons.loading} type="primary" block
>{props.lessons.loading ? "" : "加载更多"}</Button>) : <Alert
style={{ textAlign: 'center' }}
message="到底了"
type="warning"
/>
}
</section>
)
}
export default React.forwardRef(LessonList);
总结:滚动事件监听,计算每个item高度, 计算滚动的距离, 计算start end ,做数据截取,展示截取后的数据, 同时做样式绝对定位就可以 (top值就可以改变处于的高度)
虚拟列表通过只渲染可视区域的元素,显著提升了页面性能。本文介绍了虚拟列表的核心原理和实现步骤,包括监听滚动事件、计算item高度、确定显示范围、数据截取以及绝对定位等关键点。

4953

被折叠的 条评论
为什么被折叠?



