无限滚动加载时列表抖动是怎么回事?

诸葛一诺 阅读 34

我在用 React 做一个消息列表的无限滚动,每次滚动到底部就加载更多数据,但新数据一进来整个列表会突然跳一下,体验特别差。明明是 append 数据,为啥还会抖?

我试过用 scrollTop 手动保持位置,也用了固定 item 高度,但问题还在。是不是因为虚拟滚动没做?还是说我的加载时机有问题?

useEffect(() => {
  const handleScroll = () => {
    if (containerRef.current.scrollTop + containerRef.current.clientHeight >= containerRef.current.scrollHeight - 10) {
      loadMore();
    }
  };
  containerRef.current?.addEventListener('scroll', handleScroll);
  return () => containerRef.current?.removeEventListener('scroll', handleScroll);
}, []);
我来解答 赞 3 收藏
二维码
手机扫码查看
2 条解答
一正毅
一正毅 Lv1
这问题我遇到过,抖动多半是布局重排引起的。你先检查下消息项的样式定义,特别是 padding 和 margin 这些会触发重排的属性。

在 WordPress 主题开发里也经常遇到类似情况,一般我会用 position: absolute 固定滚动容器的高度和位置。你可以试试给容器加上 overflow-anchor: none 来禁用浏览器的自动滚动恢复。

另外 loadMore 函数里记得先设置一个加载状态标志,避免重复请求。数据加载完再更新列表时,可以用 requestAnimationFrame 包一下 DOM 更新操作,让渲染更平滑。

let isLoading = false;
function loadMore() {
if (isLoading) return;
isLoading = true;
// 模拟加载
setTimeout(() => {
requestAnimationFrame(() => {
// 更新列表逻辑
isLoading = false;
});
}, 500);
}


如果还是不行,考虑把虚拟滚动加进去,不过这得花点时间重构。累了就先休息会儿,这活真磨人。
点赞
2026-03-27 20:07
皇甫薪羽
这问题我之前也遇到过,本质上是数据加载后容器内容高度变了,但滚动位置没处理好。

你用的是固定高度item,但新数据进来后scrollHeight增加了,浏览器默认行为会把滚动条"重置"到顶部附近(取决于容器布局),所以会跳。

核心解决思路:加载前记录scrollHeight,加载后根据差值调整scrollTop。

const [list, setList] = useState([]);
const containerRef = useRef(null);
const loadingRef = useRef(false);

const loadMore = async () => {
if (loadingRef.current) return;
loadingRef.current = true;

const container = containerRef.current;
// 加载前记录这两个值
const oldScrollHeight = container.scrollHeight;
const oldScrollTop = container.scrollTop;

const newData = await fetchMoreData();

setList(prev => [...prev, ...newData]);

// 数据渲染完后,用 requestAnimationFrame 调整位置
requestAnimationFrame(() => {
const newScrollHeight = container.scrollHeight;
container.scrollTop = oldScrollTop + (newScrollHeight - oldScrollHeight);
loadingRef.current = false;
});
};


另一个问题:你的滚动监听没做节流,滚动到底部那一下可能触发多次,导致数据重复加载或状态混乱。加载锁(loadingRef)记得加上。

还有,加载时机判断可以稍微宽松一点,比如距离底部50px再触发,别等到完全到底,否则网络慢的时候很容易出问题:

if (container.scrollTop + container.clientHeight >= container.scrollHeight - 50) {
loadMore();
}


代码放这了,试一下应该就不抖了。
点赞
2026-03-18 15:02