虚拟列表的实现

    32

虚拟列表即可视区域渲染

例子

虚拟列表分为动态高度和固定高度

不考虑任何可变因素,实现一个最简单的例子,假定列表的li标签高度为30px,可视区域大小为300px,所以,我们在上下滚动时,可视元素为10个左右,只需要考虑如何渲染这10个元素

例子采用react实现,原生和vue同理

思路

  • 虚拟列表的表现形式和普通列表一样,但是只渲染10个元素,我们就需要一个空的元素去撑起滚动条,给这个元素虚拟列表本应该有的高度,就可以模拟滚动条
  • 虚拟列表可视区域渲染的个数时固定的,我们只需要知道当前滚动的scrollTop再除每一项的高度,就可以知道已经滚过去了多少项,拿到当前可视区域的起始序号,再总list.slice(start,end)即可
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;
}
评论区
共有评论 0
暂无评论