防抖与节流
前端页面中存在许多被频繁触发的函数,如果不限制其触发的频率,会对性能造成很大的影响,如果这些函数还涉及网络通讯,则还会对后端造成巨大的压力。
而防抖与节流是常见的两种性能优化技术,用以限制函数被触发的频率。
1.节流
什么是节流?
在前端页面中,点击一个按钮会触发一些事件。但是这些用这些事件有可能会被频繁的触发,比如说用户可能会疯狂的点击,而这样会给带来一些不合理的情况。
比如有些网站评论区,用户如果在短时间内点击了多次发布按钮,可能会出现同样的评论会重复发了好几次的情况。这显然是不合理的也十分影响性能。
而使用防抖,可以避免一个事件短时间内重复被触发。在函数调用后的一段时间内(比如0.5秒),再次点击触发,也不会生效。
有一个非常贴切的例子:王者荣耀的英雄技能CD。比如一个英雄放大招以后,那么在未来一段时间内他都不能再放大招。这期间他无论怎么点击,就算把玻璃都点碎了,也没有用。
代码
function debounce(fn, wait) {
let timer = null;
return function (...args) {
if (!timer) {
fn();
timer = setTimeout(() => {
timer = null;
}, wait);
}
};
}
使用场景
向后端交互的事件(如请求验证码、发送评论)等
2.防抖
什么是防抖?
高频率触发的事件,在指定的单位时间后才执行。如果在此期间,有重复触发了这个时间,就会重新计时。
类似于游戏中的回城机制,必须要等待几秒后才可以真的回城,如果再次期间有任何微操,回城就会被打断。
代码
function debounce(fn, wait) {
let timer = null;
return function (...args) {
//如果已经有定时器了,则取消这个定时器(里面的代码也就不会执行了)
clearTimeout(timer);
//这里会立即返回一个定时器id,在wait时间后,该定时器会自动执行里面的代码
timer = setTimeout(() => {
timer = null;
fn(...args);
}, wait);
};
}
使用场景
- input 输入事件,滚动事件或resize事件等
- 以及其他的只关心最终状态的情形
3.Demo及注意事项
其实两者没有太大区别,只要能满足自己的需求就好。
在使用过程中,记得关注this的指向。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>防抖与节流</title>
</head>
<body>
<div>
不用节流
<button onclick="log()">不用防抖触发点击事件</button><br /><br />
节流
<button onclick="log_debounce()">使用防抖触发点击事件</button><br /><br />
</div>
<div>
不使用防抖
<label>的输入监听</label><input type="text" id="text1" /><br /><br />
</div>
<div>
防抖
<label>的输入监听</label><input type="text" id="text2" />
</div>
<script>
//节流
function throttle(fn, wait = 500) {
let timer = null;
return function (...args) {
if (!timer) {
fn();
timer = setTimeout(() => {
timer = null;
}, wait);
}
};
}
let x = 1;
let log = () => {
console.log(x++);
};
let log_debounce = throttle(() => {
console.log(x++);
}, 1000);
//防抖
function debounce(fn, wait = 500) {
let timer = null;
return function (...args) {
//如果已经有定时器了,则取消这个定时器(里面的代码也就不会执行了)
clearTimeout(timer);
//这里会立即返回一个定时器id,在wait时间后,该定时器会自动执行里面的代码
timer = setTimeout(() => {
timer = null;
fn(...args);
}, wait);
};
}
let text1 = document.querySelector("#text1");
let text2 = document.querySelector("#text2");
let watch = function () {
// console.log(this);
console.log(this.value);
};
// 这里记得使用bind改变this指向
let watch_throttle = debounce(watch.bind(text2), 1000);
text1.oninput = watch;
text2.oninput = watch_throttle;
</script>
</body>
</html>