防抖和节流

性能优化

防抖和节流都是前端性能优化的方式。在前端开发中,我们经常会需要绑定一些持续触发的事件,如input、scroll、mousemove、resize等,但有些时候我们并不希望在事件持续触发的过程中那么频繁地去执行函数,这时候我们就会用到防抖和节流,让我们先看一个简单的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#count {
height: 150px;
line-height: 150px;
text-align: center;
color: #fff;
background-color: #ccc;
font-size: 80px;
}
</style>
</head>
<body>
<div id="count"></div>
<script>
let num = 1;
let oDiv = document.getElementById("count");
function count() {
oDiv.innerHTML = num++;
}
oDiv.onmousemove = count;
</script>
</body>
</html>

在上述代码中,div 元素绑定了 mousemove 事件,当鼠标在 div(灰色)区域中移动的时候会持续地去触发该事件导致频繁执行函数。效果如下:

alt

可以看到,在没有通过其它操作的情况下,函数被频繁地执行导致页面上数据变化特别快。所以,接下来让我们来看看防抖和节流是如何去解决这个问题的。

防抖(debounce)

在第一次触发事件时,不立即执行函数,而是给出一个期限值比如200ms,然后:

  • 如果在200ms内没有再次触发滚动事件,那么就执行函数
  • 如果在200ms内再次触发滚动事件,那么当前的计时取消,重新开始计时

如果短时间内大量触发同一事件,只会执行一次函数。

可设计如下的防抖函数,使得触发事件后count函数不会立即执行,而是在1s后执行,如果在1s内又触发了事件,则会重新计算函数执行时间。

1
2
3
4
5
6
7
8
9
10
function debounce(fn, delay){
let timer = null;
return () => {
if (timer){
clearTimeout(timer); //延迟时间内再次触发则清空计时器
}
timer = setTimeout(fn, delay); //开启新的计时器
};
}
oDiv.onmousemove = debounce(count, 1000);

效果如下:
alt

到这里,已经把防抖实现了,现在给出定义:

对于短时间内连续触发的事件(如上面的mousemove事件),防抖的含义就是让某个时间期限(如上面的1000毫秒)内,事件处理函数只执行一次。

还有一种防抖函数,触发事件后函数会立即执行,然后延迟时间内不触发事件才能继续执行函数,这里不再赘述,详情请看防抖和节流

节流(throttle)

所谓节流,就是指连续触发事件但是在设定的延迟时间只执行一次函数,节流会稀释函数的执行频率。

对于上面的mousemove事件,可设计如下的节流函数,在持续触发事件的过程中,count函数不会立即执行,并且每 1s 执行一次,在停止触发事件后,函数还会再执行一次。

1
2
3
4
5
6
7
8
9
10
11
12
13
function throttle(fn,delay){
let valid = true; //设置一个状态位来表示fn函数是否处于工作状态
return function() {
if(!valid) return false//fn函数不工作
valid = false;
//工作时间,执行fn函数并且在间隔期内把状态位设为无效
setTimeout(() => {
fn();
valid = true;
}, delay);
};
}
oDiv.onmousemove = throttle(count, 1000);

效果如下:

alt

从上面的动图可以看出,如果在灰色区域一直移动鼠标,那么会以1s的时间间隔,逐渐计数。

当然节流函数不止上面这一种实现方案,例如可以完全不借setTimeout,可以把状态位换成时间戳,然后利用时间戳差值是否大于指定间隔时间来做判定。也可以直接将setTimeout返回的标记当做判断条件-判断当前定时器是否存在,如果存在表示还在冷却,并且在执行fn之后消除定时器表示激活,原理都一样。详情请看防抖和节流

总结

防抖和节流都是前端性能优化的方式,防抖是控制次数,而节流是控制频率。一种常见的应用场景是浏览器的搜索引擎,对于搜索框input事件,例如要支持输入实时搜索可以使用节流方案(间隔一段时间就必须查询相关内容),或者实现输入间隔大于某个值(如500ms),就当做用户输入完成,然后开始搜索,具体使用哪种方案要看业务需求,防抖和节流可以有效避免浏览器向服务器发出过多无用的请求。

Donate
  • Copyright: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.
  • Copyrights © 2020-2021 Sanmu
  • Visitors: | Views:

请我喝杯咖啡吧~

支付宝
微信