用BetterScroll打造丝滑滚动体验的实践与避坑指南

Prog.张豪 交互 阅读 2,060
赞 11 收藏
二维码
手机扫码查看
反馈

优化前:卡得不行

最近接手了一个移动端项目,里面用到了 Better Scroll 实现列表滚动。一开始觉得这玩意儿挺简单,结果上线后用户反馈说“页面卡得像PPT”。我试了一下,确实优化前的滚动体验差到爆炸,特别是在长列表和复杂 DOM 结构下,手指滑动时能明显感觉到延迟和掉帧。

用BetterScroll打造丝滑滚动体验的实践与避坑指南

更离谱的是,有些页面在低端机上直接崩了——不是白屏,而是 Better Scroll 根本不动,完全没法滚。当时我就意识到,这个问题不解决,这个功能基本等于废了。

找到瓶颈了!

问题摆在眼前,怎么解决呢?我先用了 Chrome DevTools 的 Performance 工具跑了一下,发现几个大问题:

  • 重绘和回流太频繁:每次滚动触发了大量的 layout 和 paint 操作,特别是动态计算高度的时候。
  • 事件监听过多:Better Scroll 默认绑定了 touchmove 等事件,但这些事件在复杂 DOM 下性能开销很大。
  • 数据量太大:部分列表一次性渲染了几百条数据,直接把 DOM 节点撑爆了。

简单总结就是:DOM 太多、事件太重、计算太频繁。知道问题在哪就好办了,接下来就是一顿折腾。

优化方案一:虚拟列表拯救世界

优化的第一步,我决定引入虚拟列表(Virtual List)。这个概念其实很简单:只渲染当前视口内的内容,其他部分用占位符代替。这样一来,DOM 节点数量从几百个直接降到了十几个,效果立竿见影。

下面是优化前后的代码对比:

// 优化前:直接渲染所有数据
const list = document.querySelector('.scroll-list');
data.forEach(item => {
  const li = document.createElement('li');
  li.textContent = item.name;
  list.appendChild(li);
});

// 优化后:使用虚拟列表
const visibleCount = 10; // 视口内最多显示10条
const startIndex = Math.floor(scrollTop / itemHeight);
const endIndex = startIndex + visibleCount;

const visibleData = data.slice(startIndex, endIndex);
visibleData.forEach((item, index) => {
  const li = document.createElement('li');
  li.style.position = 'absolute';
  li.style.top = ${(startIndex + index) * itemHeight}px;
  li.textContent = item.name;
  list.appendChild(li);
});

优化后,页面滚动变得丝滑流畅,尤其是在低端机上,效果特别明显。不过这里有个踩坑点:虚拟列表的高度计算一定要精确,不然会出现滚动条错乱的问题。我试了好几次才发现,原来是因为某些样式没写对。

优化方案二:减少事件监听

Better Scroll 默认会绑定很多事件,比如 touchstart、touchmove 和 touchend。如果列表很长,这些事件会导致性能瓶颈。我的做法是手动调整配置,关闭一些不必要的事件监听。

具体代码如下:

import BScroll from '@better-scroll/core';

const bs = new BScroll('.wrapper', {
  probeType: 3, // 监听滚动位置变化
  click: false, // 关闭点击事件监听
  momentum: true, // 启用惯性滚动
  bounce: false, // 关闭边界回弹效果
});

这里有个小技巧:probeType 设置为 3,这样可以实时监听滚动位置,同时减少不必要的事件触发。另外,我把 click 设置为 false,因为这个项目不需要点击事件,关掉后性能提升很明显。

优化方案三:懒加载图片

还有一个问题是图片太多,导致页面加载时间过长。优化前,图片资源全部一次性加载,内存占用直接飙高。后来我改成了懒加载,只有当图片进入视口时才加载。

代码实现也很简单:

const images = document.querySelectorAll('.lazy-image');

function lazyLoad() {
  images.forEach(img => {
    if (img.getBoundingClientRect().top < window.innerHeight && !img.src) {
      img.src = img.dataset.src;
    }
  });
}

window.addEventListener('scroll', lazyLoad);
lazyLoad(); // 初始化时也调用一次

优化后,首屏加载时间从 5 秒降到了 800 毫秒左右,效果非常明显。不过要注意:懒加载的阈值设置要合理,否则可能会出现图片加载滞后的问题。

优化后:流畅多了

经过这一系列优化,页面的滚动性能提升了一个档次。以下是具体的性能数据对比:

  • 滚动帧率:从平均 20fps 提升到 55fps。
  • 首屏加载时间:从 5 秒降到 800 毫秒。
  • 内存占用:从 120MB 降到 40MB。

当然,也不是完全没有问题。比如虚拟列表在快速滚动时偶尔会出现轻微的闪烁,但整体来说影响不大。毕竟鱼和熊掌不可兼得嘛。

性能数据对比

最后再补充一下,整个优化过程让我深刻认识到性能优化的重要性。Better Scroll 虽然是个好工具,但如果用得不对,反而会成为性能杀手。通过虚拟列表、减少事件监听和懒加载图片这三个核心方案,我们成功解决了性能瓶颈。

以上是我个人对 Better Scroll 性能优化的完整讲解,有更优的实现方式欢迎评论区交流。这个技巧的拓展用法还有很多,后续会继续分享这类博客。希望这篇文章能帮到有同样需求的同学。

本文章不代表JZTHEME立场,仅为作者个人观点 / 研究心得 / 经验分享,旨在交流探讨,供读者参考。
发表评论

暂无评论