节流是解决频繁 mousemove 性能问题的核心方案:它按固定间隔(如 16ms)执行一次,保留移动轨迹;防抖会丢失中间状态,不适用于需过程采样的场景;手写节流函数支持 leading/trailing 控制,并需注意闭包复用、this 绑定与定时器清理。

在频繁鼠标移动事件中直接监听 mousemove 会导致大量坐标采集,严重拖慢性能。节流(throttle)是核心解法:它确保函数在指定时间间隔内最多执行一次,既保留关键移动趋势,又大幅降低调用频次。
为什么必须用节流而不是防抖?
防抖(debounce)会把连续移动“压缩”到停止后才触发一次,丢失过程中的坐标轨迹;而节流按固定节奏(如每 16ms 或 50ms)稳定输出一个采样点,适合绘制轨迹、拖拽反馈、热区判断等需要中间状态的场景。
手写一个轻量可靠的节流函数
不依赖 Lodash,几行代码即可实现带 leading(首次立即执行)和 trailing(末次补发)控制的节流:
function throttle(func, delay, options = {}) {
let lastCall = 0;
let timeoutId = null;
const { leading = true, trailing = false } = options;
<p>return function throttled(...args) {
const now = Date.now();
const elapsed = now - lastCall;</p><pre class="brush:php;toolbar:false;">if (leading && elapsed >= delay) {
func.apply(this, args);
lastCall = now;
return;
}
if (trailing && !timeoutId) {
timeoutId = setTimeout(() => {
func.apply(this, args);
lastCall = Date.now();
timeoutId = null;
}, delay - elapsed);
}}; }
在 mousemove 中正确绑定节流处理器
关键点:节流函数只需创建一次,避免每次事件都新建闭包;同时注意 this 绑定和事件参数传递:
- 用
throttle包裹你的坐标处理逻辑(如记录位置、更新 canvas、计算距离) - 推荐使用
{ passive: false }选项确保能调用preventDefault()(如需禁止默认行为) - 在组件卸载或元素移除时,记得清除可能残留的定时器(上面的
timeoutId可暴露或封装为可清理对象)
示例:
const handleMove = throttle((e) => {
console.log(e.clientX, e.clientY); // 每 32ms 最多输出一次
}, 32);
<p>element.addEventListener('mousemove', handleMove);</p>进阶建议:结合 requestAnimationFrame 更平滑
若目标是动画同步(如实时跟随光标),用 requestAnimationFrame 替代定时器更精准:
- 它与屏幕刷新率对齐(通常 60fps),无丢帧风险
- 实现方式:只在 rAF 回调中更新状态,用标志位控制是否响应本次 mousemove
- 适合做 canvas 绘制、CSS transform 跟随等视觉反馈
简单模式:
let pending = false;
element.addEventListener('mousemove', (e) => {
if (!pending) {
pending = true;
requestAnimationFrame(() => {
updatePosition(e.clientX, e.clientY);
pending = false;
});
}
});Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











