onscroll不加节流或防抖极易导致卡顿掉帧,因滚动事件高频触发且同步执行会阻塞渲染;需据场景选防抖(停手后执行)或节流(固定频率执行),并注意scrolltop兼容性写法。

直接说结论: onscroll 本身只是个事件绑定入口,不加节流或防抖就直接用,90% 的场景会卡顿、掉帧、甚至阻塞主线程 —— 尤其是监听 window 滚动时。
为什么 window.onscroll 容易让页面变卡
滚动不是“按一下触发一次”,而是每毫秒都可能触发多次。Chrome 下快速滚动,scroll 事件每秒能触发 60~120 次;如果回调里有 scrollTop 计算 + DOM 查询 + 样式重排,浏览器根本来不及渲染。
- 典型错误现象:
console.log('scroll')打印出几百条,但页面动画明显卡顿、输入框失焦、position: sticky失效 - 真实影响:不是“看起来慢”,而是强制同步执行导致
requestAnimationFrame被挤占、input事件延迟响应 - 兼容性陷阱:
document.body.scrollTop在 Chrome/Firefox 新版本已弃用,只靠它取值会返回0;必须用document.documentElement.scrollTop || window.scrollY
防抖(debounce)适合什么场景
防抖的核心逻辑是“等用户停手再干活”,适用于结果只关心最终状态的场景。
- 搜索框联想:用户还在输,不发请求;停 300ms 后才查
- 回到顶部按钮显隐:只在用户停止滚动后,判断是否
scrollY > 200,再显示按钮 - 懒加载图片:滚动停止后,遍历所有待加载
img,检查是否进入视口
简单防抖实现(不用第三方库):
function debounce(fn, delay) {
let timer
return function (...args) {
clearTimeout(timer)
timer = setTimeout(() => fn.apply(this, args), delay)
}
}
window.addEventListener('scroll', debounce(() => {
const y = window.scrollY
if (y > 200) document.getElementById('back-top').style.display = 'block'
}, 150))
节流(throttle)该什么时候用
节流是“固定频率执行”,适合需要持续反馈的场景,比如实时计算滚动进度、视差动画、滚动位置高亮导航项。
- 常见误用:用节流做无限加载 —— 用户拖到底部还没松手,就提前触发了加载,但数据还没到视口底部
- 正确做法:节流 + 底部距离判断组合使用,例如每 100ms 检查一次
scrollY + innerHeight >= scrollHeight - 50 - 性能注意:节流间隔别设太小,
50ms已接近 20fps,人眼可感知卡顿;推荐100ms起步
轻量节流写法(无依赖):
function throttle(fn, delay) {
let last = 0
return function (...args) {
const now = Date.now()
if (now - last > delay) {
fn.apply(this, args)
last = now
}
}
}
window.addEventListener('scroll', throttle(() => {
const progress = (window.scrollY / (document.body.scrollHeight - window.innerHeight)) * 100
document.querySelector('.progress-bar').style.width = `${progress}%`
}, 100))
真正难的不是写防抖或节流函数,而是每次加 scroll 监听前,先问自己一句:这个逻辑,是“最后一次滚动后才需要”还是“滚动中就要持续响应”?选错策略,优化就变成负优化。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











