Vue项目中使用IntersectionObserver实现加载进度条导致滚动卡顿怎么办?

设计师硕辰 阅读 14

在Vue项目里想用IntersectionObserver检测关键资源加载进度,然后发现滚动时页面卡顿,特别是资源较多时更明显。我尝试给每个资源元素添加了观察器,然后在回调里计算总进度:


const observer = new IntersectionObserver((entries) => {
  let loaded = 0;
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      loaded += entry.target.dataset.size;
      updateProgress(loaded / totalSize);
    }
  });
}, { rootMargin: '0px', threshold: 1.0 });

resources.forEach(el => observer.observe(el));

但滚动到密集元素区域时,fps会突然掉到20多。试过把计算逻辑抽到requestAnimationFrame里也没好转,是不是观察器实例太多导致的?有没有更高效的方式实现加载进度同步?

我来解答 赞 12 收藏
二维码
手机扫码查看
1 条解答
Tr° 文茹
你这个问题确实是 IntersectionObserver 实例太多导致的,而且在密集元素区域频繁触发回调,加上你在里面做计算和更新 UI,很容易造成性能瓶颈。每个 Observer 实例都有开销,尤其是你给几十上百个元素都绑一个,浏览器根本扛不住。

其实不需要为每个资源单独创建 Observer,可以用一个共享实例来观察所有元素,这才是标准做法。另外你现在的 threshold 是 1.0,意味着必须完全进入视口才触发,这会导致体验延迟,也不利于平滑更新进度。

你可以这样改:

let loaded = 0;
const totalSize = resources.reduce((sum, el) => sum + Number(el.dataset.size), 0);
const seenElements = new Set();

const observer = new IntersectionObserver(
(entries) => {
let shouldUpdate = false;
entries.forEach((entry) => {
if (entry.isIntersecting && !seenElements.has(entry.target)) {
seenElements.add(entry.target);
loaded += Number(entry.target.dataset.size);
shouldUpdate = true;
}
});
if (shouldUpdate) {
// 使用 requestIdleCallback 或 rAF 控制更新频率
requestAnimationFrame(() => {
updateProgress(loaded / totalSize);
});
}
},
{ rootMargin: '50px', threshold: 0.1 } // 提前触发,降低阈值更灵敏
);

resources.forEach(el => observer.observe(el));


关键点:

- 全局一个 Observer 实例就够了,别每个元素搞一个
- 用 Set 记录已见过的元素,避免重复计算
- rootMargin 加载缓冲区,threshold 调低一点让触发更早更平滑
- 把 updateProgress 放进 rAF,避免高频更新 DOM

这样更清晰,也能把 FPS 拉回来。如果还觉得卡,可以把 updateProgress 做节流,比如每 100ms 最多更新一次,用户也感知不到差异。
点赞 3
2026-02-12 06:00