原理: 只渲染可视节点以减少DOM开销,提升加载速度

以下代码是伪代码,具体实现看这

如果你想在项目中使用复杂的虚拟滚动,可以看下react-virtualized 。本文只提供虚拟列表实现思路

实现步骤

创建容器

<div className="container">
  <div className="virtual-container" />
</div>            

.container {
  height: 200px;
  width: 200px;
  border: 1px solid black;
  overflow-y: scroll;
}
.virtual-container {
  position: relative;
  overflow: hidden;
}

计算高度

通过每行高度乘以数据长度计算高度。这里是每行高度固定的情况,如果不固定可以参考实现中virtual-list-autoheight.js文件的实现(高度不固定,但一定要知道行的最大高度)

 const containerHeight = itemHeight * datasource.length;
 refVirtualContainer.current.style.height = containerHeight + "px";

 

实现滚动效果

因为我们只渲染一部分数据,无数据部分不能滚动,所以需要自己实现滚动。

我们利用transformY实现滚动效果

const refVirtualContainer = useRef();
const [scrollDis, setScrollDis] = useState(0);

useEffect(() => {
    refContainer.current.addEventListener("scroll", (e) => {
      setScrollDis(scrollTop);
    });
}, [])

<div className="virtual-container" ref={refVirtualContainer}>
 <div className="virtual"   style={{ transform: `translateY(${scrollDis}px)` }}/>
</div>   

 

更新数据

通过以上两步我们实现了可滚动的列表,之后需要在每次滚动时更新数据即可。

具体实现

const [dataSlice, setDataSlice] = useState([]); //数据切片   

refContainer.current.addEventListener("scroll", (e) => {
   setData(e.target.scrollTop, refContainerHeight, containerHeight);
});

const setData = (scrollTop, refContainerHeight, containerHeight) => {
    const beginNum = Math.ceil(
      (scrollTop / containerHeight) * datasource.length
    );
    const domNum = Math.ceil(refContainerHeight / itemHeight);
    setDataSlice(datasource.slice(beginNum, domNum * 2 + beginNum));
  };

   {dataSlice.map((item, idx) => (
    <div
      key={idx}
      className="item"
      ref={refItem}
      style={{ height: itemHeight, lineHeight: `${itemHeight}px` }}
    >
       <Button>{item * 80}</Button>
    </div>
  ))}

注意

  • 我们把key设置成idx,这样就能利用react机制使节点复用,减少性能消耗
setDataSlice(datasource.slice(beginNum, domNum * 2 + beginNum));
  • 这一段中 domNum * 2是为了在滚动时去掉闪烁,可以去掉试一下效果

 

 

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐