返回

重新认识定高的虚拟滚动,见识技术大有门道

前端

给予好环境和机会,定高的虚拟滚动或将胜过不定高的虚拟滚动

如果说,固定高度的长列表渲染的优化方法,毫无疑问就是使用虚拟滚动。但如果说使用虚拟滚动的方案,基本都是在说使用定高的虚拟滚动,由于整个列表的高度已经提前已知,所以做法也很简单,就是按照可视区域的窗口大小计算总高度的倍数,作为整个列表的高度,然后在使用的时候只渲染可视区域内的元素,并动态改变列表的位置,从而实现无限滚动的效果。

可以说,这种方案非常简单、高效,并且很容易实现。但这里的前提是,你确定了列表的高度是固定的,因为只有这样,虚拟滚动方案计算出来的位置才是准确的,那么虚拟滚动方案才能有效。在绝大多数情况下,这种方案其实是没有问题的,但总有例外。

比如说,电商网站的商品列表,商品的可能有长有短,虽然单行的高度是确定的,但多行的情况下整个商品的高度就不确定了。

这个时候,不定高的虚拟滚动方案就派上了用场。

不定高的虚拟滚动方案,主要是针对内容高度不固定且数量较大的列表,也是采用只渲染可见区域元素的优化方案,不同的是,它采用的是高度自适应的方案。当然,这个时候为了能够准确的确定列表的滚动位置,你需要增加一个高度计算的逻辑。

这个逻辑的原理也很简单,一开始只渲染可视区域内的元素,当用户向下滚动列表时,就开始动态计算当前可视区域内最后一个元素的绝对位置。假设这个位置为x,那么整个列表的总高度就应该是x加上已经渲染的部分的高度。

可能有人会说,这样的话,滚动条就不是固定的了,这样更难使用,其实这里有一个小技巧可以解决这个问题,我们给虚拟滚动加上一个固定高度的外层容器,并且设定一个最大高度,这样的话,滚动条就不会是无限的了。

下面我们举一个代码的例子,假设我们有个这样的列表:

<div class="container">
  <div class="list-item">Item 1</div>
  <div class="list-item">Item 2</div>
  <div class="list-item">Item 3</div>
  ...
</div>

我们要实现不定高的虚拟滚动,可以按照以下步骤进行:

  1. .container容器上添加一个事件监听器,监听scroll事件。
  2. 在事件监听器中,获取当前可视区域内最后一个元素的绝对位置。
  3. 计算整个列表的总高度,并将其设置到.container容器的height属性上。
  4. 渲染可见区域内的元素。

具体的代码如下:

const container = document.querySelector('.container');
const listItems = document.querySelectorAll('.list-item');

container.addEventListener('scroll', () => {
  const lastListItem = listItems[listItems.length - 1];
  const lastListItemOffsetTop = lastListItem.offsetTop;
  const containerHeight = lastListItemOffsetTop + lastListItem.offsetHeight;

  container.style.height = `${containerHeight}px`;
});

renderVisibleItems();

这样的话,我们就可以实现不定高的虚拟滚动列表了。