虚拟列表初探

前言

虚拟列表,顾名思义,不是真实的列表,它一般只存在于人类的美好遐想之中…

跑题了,重来。

在数据量爆发的今天,很多时候一个选择器可能从服务器加载非常多的数据,然而浏览器在一次性把这些数据渲染到页面上的时候会出现卡顿甚至是崩溃的情况,所以虚拟化技术应运而生,为了更好的用户体验和更好的使用体验。

这话是引用element-plus中的,我觉得说的挺有道理,就小小地借鉴一下。虚拟列表的出现是为了解决列表中数据内容过长,过多,导致的加载缓慢和快速滚动白屏。

开始

首先,先来看一个例子
我们再container中,动态添加20000个列表项,高度固定24,会发现页面加载时,出现了明显的延迟卡顿。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <style>
    #container {
        width: 240px;
        height: 300px;
        margin-top: 24px;
        overflow-y: auto;
    }
  </style>
</head>
<body>
<div>list-container: </div>
<div id="container"></div>
<script>
  const NUMBER = 20000, HEIGHT = 24;
  const container = document.getElementById('container');
  let str = ``;
  for (let i = 0; i < NUMBER; i++) {
    let s = ``;
    s += `
      <div
        style="height: 24px;display: flex;align-items: center;box-sizing: border-box
        justify-content: space-between;font-size: 14px;border-bottom: 1px solid #ddd;"
      >
        <div>list</div>
        <div>${i}</div>
      </div>
    `
    str += s;
  }
  container.innerHTML = str;
</script>
</body>
</html>
复制代码

简单的一个列表需要0.8秒的时间,而且拖动滑块快速滑动时,有时还会出现短暂的空白情况。

1.png

解决

为了更好的客户体验,我们可以使用虚拟列表来解决这个问题。

原理很简单,监听滚动事件时,我们通过视窗的高度,和滚动条的滚动距离,来计算出当前应该显示给使用者的列表,并在前后补充几个作为缓冲。

然后根据滚动距离,通过 定位 或者 translate偏移 将列表移动到当前显示的位置。在客户的感知中,与正常列表无异,而实际上,我们一直只渲染了这十几二十个列表项。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <style>
      #container {
          width: 240px;
          height: 300px;
          margin-top: 24px;
          overflow-y: auto;
      }
      #list-container {
          position: relative;
      }
      #scroll-div {
          width: 100%;
          position: absolute;
          top: 0;
          left: 0;
      }
  </style>
</head>
<body>
<div>list-container: </div>
<div id="container">
  <div id="list-container">
    <div id="scroll-div"></div>
  </div>
</div>
<script>
  const NUMBER = 20000, HEIGHT = 24;
  const container = document.getElementById('container');
  const scrollDiv = document.getElementById('scroll-div');
  const listContainer = document.getElementById('list-container');
  listContainer.style.height = NUMBER * HEIGHT + 'px';
  const height = container.offsetHeight;
  // 取超过高度的数量
  const num = Math.ceil(height / 24);

  // 滚动时候的回调函数
  const scrollCallback = function(e) {
    const h = e ? e.target.scrollTop : 0;
    console.log('h', h);
    // 找到该显示列表中的第几个,然后加上num,再往前取5个,往后取5个,得到需要渲染的列表
    const n = Math.ceil(h / HEIGHT);
    const start = n > 5 ? n - 5 : 0;
    const end = n + (num - 1) > NUMBER - 5 ? NUMBER : n + (num - 1) + 5;
    let str = ``;
    for (let i = start; i < end; i++) {
      let s = ``;
      s += `
        <div
          style="height: 24px;display: flex;align-items: center;box-sizing: border-box;
          justify-content: space-between;font-size: 14px;border-bottom: 1px solid #ddd;"
        >
          <div>list</div>
          <div>${i}</div>
        </div>
      `;
      str += s;
    }
    const top = start * HEIGHT;
    scrollDiv.style.top = top + 'px';
    scrollDiv.innerHTML = str;
  }
  // 初始化
  scrollCallback();
  // 监听滚动事件
  container.onscroll = scrollCallback;
</script>
</body>
</html>
复制代码

2.png

结语

仅仅是一次简单的虚拟列表的实现,因为高度都是固定的,十分有局限性。由于这次是初探,宽以待己,争取下次中探时候,能找到自适应高度的良好解决方法。

由于最近沉迷炉石战棋,加上对flutter继续“坚持不懈”的探索与学习,我终于上到了5500分。导致这个文章写得太少太浅,深感愧疚,自我鞭策一下,以后尽量还是保证一礼拜至少写一篇吧。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享