虚拟列表的实现
- 0
#讨论区
00条评论
实时对话
loading...
虚拟列表即可视区域渲染
虚拟列表分为动态高度和固定高度
不考虑任何可变因素,实现一个最简单的例子,假定列表的li标签高度为30px,可视区域大小为300px,所以,我们在上下滚动时,可视元素为10个左右,只需要考虑如何渲染这10个元素
例子采用react实现,原生和vue同理
思路
list.slice(start,end)
即可jsx
css
import { useEffect, useRef, useState, Profiler } from "react";
import "./styles.css";
const total = 10000;
const arr = Array.from({ length: total }).map((_, idx) => idx);
export default function App() {
const appRef = useRef();
const [start, setStart] = useState(0);
const [scrollTop, setScrollTop] = useState(0);
const [offset, setOffset] = useState(0);
const [list, setList] = useState([]);
const onScroll = (e) => {
requestAnimationFrame(() => {
setScrollTop(e.target.scrollTop);
let start = Math.floor(e.target.scrollTop / 30);
setStart(start);
let end = start + 10;
setList(arr.slice(start, end));
});
};
useEffect(() => {
const container = appRef.current;
container.addEventListener("scroll", onScroll);
}, []);
useEffect(() => {
setOffset(start * 30);
}, [start]);
function callBack(...arg) {
console.log(arg[2]);
}
return (
<div>
{scrollTop}
<Profiler id="vl" onRender={callBack}>
<div ref={appRef} className="App">
<div className="mask" style={{ height: "300000px" }}></div>
<ul style={{ transform: `translate3d(0, ${offset}px, 0)` }}>
{list.map((item) => (
<li key={item}>{item}</li>
))}
</ul>
</div>
</Profiler>
</div>
);
}
.App {
font-family: sans-serif;
text-align: center;
background-color: aqua;
height: 300px;
overflow-y: auto;
position: relative;
}
.mask {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
ul {
transform: translate3d(0, 0, 0);
}
li {
height: 30px;
}
li:nth-of-type(odd) {
background-color: #f00;
}
li:nth-of-type(even) {
background-color: #0f0;
}