通常,scroll 事件,resize 事件、鼠标事件(比如 mousemove、mouseover 等)、键盘事件(keyup、keydown 等)都存在被频繁触发的风险。频繁触发回调导致的大量计算会引发页面的抖动甚至卡顿。
无论是公司项目,还是个人项目,往往会遇到这样的一些造成回调开销加大的问题,比如重复提交、快速点击、高频事件等。
为了规避这种情况,我们需要一些手段来控制事件被触发的频率。然而,节流(throttle)与防抖(debounce)是我们在日常开发中常用的优质代码片段,能够给我们的项目带来性能的提升。
问题
小程序端以答题考试知识库项目为例,存在的问题:
1)重复提交
单位时间内,多次请求,造成的后台重复录入现象。
2)快速点击
单位时间内,快速点击查询或者答题,触发异步事件,造成数据与ui混乱的现象。
3)高频事件
单位时间内,触发的各类事件,且响应速度低于请求,造成的卡顿或其他混乱现象。
节能知识竞赛-提交答卷:
考研知识库-模糊查询:
人物评选投票-精准搜索:
解决方案
一般在项目中比较常见用的方式是事件或函数的节流(throttle)与防抖(debounce)。
节流(throttle)与防抖(debounce)的本质
这两个东西都以闭包的形式存在。
它们通过对事件对应的回调函数进行包裹、以自由变量的形式缓存时间信息,最后用 setTimeout 来控制事件的触发频率。
节流(throttle)
节流:每间隔某个时间去执行某函数。
节流(throttle) 的中心思想在于:在某段时间内,不管你触发了多少次回调,我都只认第一次,并在计时结束时给予响应。
function throttle(fn: Function, delay: number) {
let timer: any = null;
return function () {
if (timer) return;//如果定时器还在,说明上一次延迟执行还没有完成,则不再执行
timer = setTimeout(() => {
fn.apply(this, arguments);
timer = null;
}, delay);
};
}
防抖(debounce)
防抖:将几次操作合并为一次操作进行。
防抖(debounce)的中心思想在于:我会等你到底。在某段时间内,不管你触发了多少次回调,我都只认最后一次。
function debounce(fn: Function, delay: number) {
let timer: any = null;
return function () {
if (timer) {
clearTimeout(timer);//如果方法多次触发,则把上次记录的延迟执行代码清掉,重新开始
}
timer = setTimeout(() => {
fn.apply(this, arguments);
timer = null;
}, delay);
};
}
总结
防止事件频繁触发回调函数的方法有很多,其中防抖和节流是现在比较主流的处理方式。
节流和防抖动最大的区别就是,节流函数不管事件触发有多频繁,都会保证在规定时间内一定会执行一次真正的事件处理函数,而防抖动只是在最后一次事件后才触发一次函数。
调用的时候怎么解决 this的问题呢