前言
DOM 事件中有很多高频操作,比如 onscroll 滚动监听,input 输入值监听,获取屏幕尺寸 resize 等。在实际应用场景中还有防止按钮多次点击。那么怎么减少高频事件的执行和阻止按钮多次点击昵?最好的方案就是防抖与节流。
防抖
上一个定时器没有执行完,直接清除定时器,开启下一轮定时器。将多个操作优化为只在最后一次执行。
使用场景:用户输入。输入完成之后做一次校验即可。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<title>React App</title>
</head>
<body>
<input type="text" id="input">
</body>
<script>
function sayHi(ages) {
console.log(ages);
}
function debounce(fn,time,immediate) {
var timer = null ;
var context = this;
var callNow = immediate;
return function(...args){
//立即执行
if (immediate) {
fn.apply(context, args);
callNow = false;
}
//当有定时器时,先将定时器清掉
if(timer) {clearTimeout(timer)}
//然后开启下一轮定时器
timer = setTimeout(()=>{
fn.apply(context,arg);
},time)
}
}
var input = document.getElementById('input');
input.addEventListener('input', debounce(sayHi,1000,'防抖')); // 防抖
</script>
</html>
节流
上一个定时器没有执行完,直接返回,等待执行完成之后,再开启下一轮定时器。
使用场景: 滚动条事件 或者 resize 事件,通常每隔 100~500 ms执行一次即可。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<title>React App</title>
</head>
<body>
<input type="text" id="input">
</body>
<script>
function sayHi(ages) {
console.log(ages);
}
function throttle(fn,time,immediate) {
var timer = null;
var context = this;
var callNow = immediate;
return function(...arg){
if (callNow) {
fn.apply(context, args)
callNow = false
}
//只有当上一轮定时器走完才开启下一轮
if(!timer){
timer = setTimeout(()=>{
fn.apply(context,arg);
clearTimeout(timer);
timer = null;
},time)
}
}
}
var input = document.getElementById('input');
input.addEventListener('input', throttle(sayHi,500,'节流')); // 节流
</script>
</html>
区别
不管是防抖还是节流,其根本原理都是通过定时器来阻止高频事件。区别在于:
- 防抖只是为了防止多个定时器同时执行造成的抖动或者卡顿。所以在没有开启下一轮定时器时,都需要先将已存在的定时器清掉。这样就不会同时存在多个定时器。
- 节流是在防抖的基础上对定时器时间有更高的要求。必须等到上一轮定时器走完才可开启下一轮定时器。
- 假设定时器为 3s,防抖是走到1s,2s的时候把定时器清除,继续开启下一轮定时器,继续走1s,2s,,。节流是走到 1s,2s 的时候直接返回。非要走完1s,2s,3s 方可开启下一轮定时器。
- 防抖:1,2 || 1,2,3 || 1 || 1,2,3;
节流:1,2,3 || 1,2,3 || 1,2,3 || 1,2,3。