touchmove事件需节流而非防抖以保障滑动实时反馈,推荐24ms时间戳节流;防抖仅用于touchend后收尾动作;须preventDefault、缓存布局计算、清除定时器防泄漏。

移动端触摸滑动(touchmove)事件触发极频繁,每秒可达 60–120 次,直接绑定处理逻辑极易造成主线程阻塞、手势卡顿、页面掉帧。防抖和节流不是“都能用”,而是要严格按交互意图选:滑动中需实时反馈(如拖拽、滚动跟随),必须用节流;滑动结束后才执行动作(如松手惯性计算、最终位置判定),才适合防抖。
节流更适合 touchmove 的持续响应
滑动过程中的位置采样、视差更新、拖拽位移计算等,要求稳定节奏输出,不能等“停了再算”。节流能保证每 16–32ms 最多执行一次,匹配屏幕刷新率,避免过度计算又不丢关键帧。
- 推荐时间戳版,轻量无定时器开销:
const handleMove = throttle((e) => {
const touch = e.touches[0];
updatePosition(touch.clientX, touch.clientY);
}, 24); // 24ms ≈ 42fps,兼顾流畅与性能 - 避免设低于 16ms:比渲染还快反而增加调度负担,Chrome DevTools Performance 面板可见大量 Task 堆积
- 务必透传
this和触点参数:用fn.apply(this, [e])或箭头函数 + 展开,别写成fn(e)导致this指向丢失
防抖只用于滑动结束后的收尾动作
用户手指抬起(touchend)后需要判断是否为快速滑动、是否触发回弹、是否跳转锚点——这类操作只关心最终状态,中间过程全可忽略。
- 典型用法:
const onEnd = debounce(() => {
if (velocity > threshold) scrollBy(0, -velocity * 2);
}, 150);
element.addEventListener('touchend', onEnd); - 慎用 immediate = true:滑动中无法预判终点,首次触发就执行会误判(比如刚下拉就触发加载)
- 搭配
touchcancel一起绑定:防止用户中途取消手势导致防抖计时器残留
实际组合使用更贴近真实场景
一个完整的手势控制常需两者协同。例如实现下拉刷新:
立即学习“Java免费学习笔记(深入)”;
- 滑动中用节流更新下拉层位移和下拉进度条
- 松手瞬间用防抖判断是否达到阈值并触发刷新逻辑
- 刷新开始后,立即清除所有节流/防抖闭包中的 timer / lastTime,避免内存泄漏(尤其 SPA 页面切换时)
绕不开的细节陷阱
移动端还有额外注意点:
-
touchmove默认会触发页面滚动,若自定义拖拽,请加e.preventDefault(),但要在节流函数内部加,否则可能拦截过早或过晚 - 不要在节流/防抖函数里反复调用
getBoundingClientRect()或offsetTop:这些是强制同步布局(reflow),放在节流外预先缓存更安全 - iOS Safari 对
touchmove的触发频率限制更严,建议节流间隔不低于 20ms,避免被系统降频


















