我们业务中,经常遇到过需要进行虚拟滚动的场景。 那么首先整体思路和图片懒加载类似,当触达达到可视区域,才进行元素渲染
方案
- 定高度
- 不定高度
定高度
1, 对于定高度相对好处理,首先,对于滚动区域,首先分为三个部分,一个是上缓冲区,另一个是下缓冲区,以及可视区域。 2. 制造缓冲区,都是为了能解决,滚动过快导致的白屏问题。
思路分析
- 首先确定一个最外层的container 容器。
- 之后设定三个区域,上缓冲区域,可视区域,下缓存区域。
- 计算每个元素距离最顶部容器的距离,以及各个区域的起始,结束序列号。
- 将上,下缓冲区内的元素,全部都塞进container 里面,通过序列,索引就能精确计算出,可视元素的具体相对位置。
疑问
- 需要渲染的节点越多,性能越差?
- 这个问题,要从渲染的速度,以及所需空间来考虑,一个是dom树的构建,布局树,重绘重流问题,以及过多的dom也会占用更多的内存空间,并且如果大量的dom没有及时清除,可能会导致内存泄露等问题。
demo代码示例
import React, { useEffect, useState } from "react";
type Props = {
//可视区域高度
height?: number;
//容器宽度
width?: number;
//每一项高度
itemSize?: number;
// 总数量
itemCount?: number;
children?: React.ReactNode;
};
export const Lazy = (props: Props) => {
const { itemSize = 10, height, itemCount, children, width } = props;
const [scrollTop, setScrollTop] = useState(0);
const handleScroll = (e: any) => {
setScrollTop(e.currentTarget.scrollTop);
};
const getContainer = (scrollTop: number) => {
//可视区域上边界
let startIndex = Math.floor(scrollTop / parseInt(itemSize));
// 缓冲区域上边界
let cacheStartIndex = startIndex - 2;
// 可视区能展示的元素的最大个数
const numVisible = Math.ceil(height / itemSize);
//下缓冲区结束边界
let cacheEndIndex = startIndex + numVisible + 2;
let items = [];
for (let i = cacheStartIndex; i < cacheEndIndex; i++) {
items.push(
<div
style={{ position: "absolute", top: i * itemSize, height: itemSize }}
>
{i}
</div>
);
}
return items;
};
return (
<div
style={{
height: height,
width: props.width,
position: "relative",
overflow: "auto",
}}
className="container"
onScroll={(e) => {
handleScroll(e);
}}
>
{getContainer(scrollTop)}
</div>
);
};