滚动事件优先选节流而非防抖,因其能定期响应连续滚动,不错过懒加载、吸顶等关键时机;推荐用requestanimationframe封装节流,天然对齐60fps且无丢帧风险。

高频滚动事件(scroll)极易引发性能问题,比如频繁重排重绘、卡顿、CPU 占用飙升。防抖和节流都能缓解,但**滚动场景下优先选节流**——因为用户持续滚动时,你通常需要“定期响应”,而不是等他彻底停下才处理。
为什么滚动事件更适合节流?
滚动是连续动作,用户可能快速滑动一整页。防抖会等到滚动完全停止才执行,导致中间关键时机(如懒加载临界点、吸顶导航切换、视差效果更新)被错过;而节流能保证每 16ms(≈60FPS)或自定义间隔内稳定执行一次,既控制频率,又不丢响应。
- 防抖在滚动中只适合极少数需求:比如“滚动到底部后加载更多”,需确认用户真正停下了
- 节流适用于绝大多数滚动交互:吸顶导航、滚动进度条、图片懒加载判断、实时视差位移
- 浏览器原生
requestAnimationFrame是比定时器更优的节流底层方案,它自动对齐屏幕刷新节奏
推荐的节流实现方式(兼顾性能与兼容性)
不用自己手写带标志位或时间戳的复杂逻辑,直接用 requestAnimationFrame 封装最稳妥:
Java JDK 25 来自 OpenJDK 官方归档,版本为 JDK 25,本条下载地址已指向官方 Windows x64 zip 安装包直链,适合调试旧项目或兼容旧版 Java 运行环境。
function throttleRAF(fn) {
let isQueued = false;
return function(...args) {
if (!isQueued) {
isQueued = true;
requestAnimationFrame(() => {
fn.apply(this, args);
isQueued = false;
});
}
};
}
// 使用示例
window.addEventListener('scroll', throttleRAF(() => {
const scrollTop = window.pageYOffset;
// 执行吸顶、懒加载判断等轻量逻辑
if (scrollTop > 100) {
document.body.classList.add('scrolled');
}
}));
- 它天然以屏幕刷新率(通常 60Hz)为节流周期,比
setTimeout更精准、无丢帧风险 - 避免了时间戳计算误差和定时器堆积问题
- 无需手动清理,函数执行完自动释放
防抖在滚动中怎么用才合理?
如果真要用防抖,只用于明确需要“终态判断”的操作,例如:
- 滚动结束后重新计算布局(如瀑布流容器高度重排)
- 用户松开滚轮/手指后触发一次统计上报
- 搜索结果页滚动到底部,且静止 300ms 后才请求下一页
写法示例(带立即执行可选):
function debounce(fn, delay, immediate = false) {
let timer = null;
return function(...args) {
if (immediate && !timer) {
fn.apply(this, args);
}
clearTimeout(timer);
timer = setTimeout(() => {
if (!immediate) fn.apply(this, args);
timer = null;
}, delay);
};
}
// 滚动停止 300ms 后加载更多
window.addEventListener('scroll', debounce(() => {
if (isAtBottom()) loadMore();
}, 300));
实际优化还要配合 DOM 和渲染策略
光靠节流/防抖不够,必须叠加其他手段:
-
避免在节流回调里读写布局属性(如
offsetTop、getBoundingClientRect()),它们会强制同步回流。改用IntersectionObserver判断元素是否进入视口 -
把耗时操作移出主线程:复杂计算用
Web Worker,动画用CSS transform而非top/left -
监听时加
{ passive: true },告诉浏览器该滚动事件不会调用preventDefault(),大幅提升滚动流畅度
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










