无限滚动加载时列表抖动怎么办?

___怡冉 阅读 33

我用 IntersectionObserver 做了个无限滚动,但每次加载新数据后页面会突然跳一下,体验很糟糕。明明没动滚动位置,怎么就抖了?

试过给容器加固定高度、用虚拟滚动库,但要么没效果要么太重。是不是我结构写得有问题?

<div id="scroll-container" style="height: 80vh; overflow-y: auto;">
  <div v-for="item in list" :key="item.id" class="item">
    {{ item.title }}
  </div>
  <div id="sentinel"></div>
</div>
我来解答 赞 13 收藏
二维码
手机扫码查看
2 条解答
UI馨予
UI馨予 Lv1
这个问题的根源是:加载新数据后,列表总高度增加了,但你的滚动容器可视区域没变,所以之前看到的内容被"挤"下去了,视觉上就是跳一下。

给你一个最直接的解决方案——保存并恢复滚动位置:

const scrollContainer = document.getElementById('scroll-container');

// 加载新数据前保存当前滚动位置和容器高度
const oldScrollTop = scrollContainer.scrollTop;
const oldScrollHeight = scrollContainer.scrollHeight;

// 你的加载逻辑
list.value.push(...newItems);

// 等DOM更新后
await nextTick();

// 恢复位置,新滚动位置 = 原位置 + 新增的高度
scrollContainer.scrollTop = oldScrollTop + (scrollContainer.scrollHeight - oldScrollHeight);


这样即使内容变多了,用户看到的还是同一批内容,不会有跳动感。

如果你用的是 Vue,可以把这段逻辑封装成一个函数,在 IntersectionObserver 的回调里调用。

还有一个更省心的做法:别在 sentinel 之前插入数据,在列表末尾追加。看起来你代码里是用 v-for 渲染的,只要 push 到数组末尾就行,这样 sentinel 会被自然推下去,不会触发奇怪的抖动。

试试看,应该能解决。
点赞 1
2026-03-11 23:04
技术晓燕
我之前踩过这个坑,折腾了好久才发现原因。

你这个抖动大概率是两个原因之一:

第一,列表项高度不固定。如果你的 item 里面有图片或者异步加载的内容,渲染完成后会把下面的内容挤下去,视觉上就是"跳一下"。给图片或者容器设个固定高度或者最小高度就行,留好占位空间。

第二,也是我当年踩的那个坑,你的数据是往数组头部插入的吧?比如用的 unshift 或者把新数据拼到前面。这样会导致已有的 DOM 节点位置整体下移,滚动位置就乱了。

如果是第二种情况,最简单的解法是用 CSS 的 overflow-anchor 属性。给你的列表容器加上这个:

#scroll-container {
overflow-anchor: auto;
}

.item {
overflow-anchor: none;
}


这样浏览器会自动锁定滚动锚点,新内容插入时不会影响当前滚动位置。

或者如果你是往头部插数据,可以用个笨办法,手动修正滚动位置:

// 插入数据前记录
const oldHeight = container.scrollHeight;

// 插入数据后修正
this.$nextTick(() => {
const newHeight = container.scrollHeight;
container.scrollTop += newHeight - oldHeight;
});


我看你代码里用的是 Vue,还要注意一点,key 别用 index,用 item.id 就对了。用 index 当 key 会导致 Vue 复用错 DOM 节点,也会引起奇怪的跳动。

先排查下是不是这几个问题,不行再贴完整代码我帮你看。
点赞 2
2026-02-28 16:00