防抖和节流
每次看到防抖和节流都要重新去撸一下基本概念,才能对号入座,所以想添加一些深入思考,让自己对这 两个名词能够刻骨铭心并应用到实践中。
抖动是什么意思?什么情况下会带来抖动?防止抖动的逻辑是什么?具体怎么实现?这些都是曾经在我脑海中存在的疑惑。
抖动含义
类似身体的抖动,网页也会有抖动,所谓的页面抖动就是浏览器频繁布局时,由于计算能力不足导致的页面颤动现象。防止抖动就是利用类似于减少回调执行次数的手段,对短时间内的重复调用进行忽略,避免浏览器发生抖动现象的技术。
节流含义
节流,百度百科的词语解释是:流体在管道内流动,突然遇到截面变窄,而使压力下降的现象。
JS 范畴内的节流目的也是降低压力,降低服务器的压力,降低客户端计算的压力。
相同
- 节流和防抖都是都是以节省短时间内回调的调用次数为宗旨,大神们想出来的性能优化对策。
- 都是对回调函数进行包装
- 都利用闭包和计时器实现
不同
-
1、执行时机不同
目标一致,设计思想上一脉相承, 但是脑回路稍有差别。节流是在某段时间内只执行首次回调,防抖通常是只执行末次回调。 -
2、应用场景的不同:
执行时机不同直接决定了应用场景的不同。
假如我们监听页面滚动事件,然后有一个页面滚动的回调用来调整页面的内容呈现,当页面在1秒内滚动500像素时,如果不做限制,回调可能被执行500次,用户看到的内容将高频次在变化,类似于电影被以高倍速播放,这样用户体验能好吗?这时候减少短时间内会导致局部或者整体页面有视觉上更新的回调的调用,指定事件间隔内只在最后一次触发回调时执行,直接让页面呈现出我们要想要看到的移动500像素后的内容,省去中间环节,可以减小客户端的计算压力,避免页面的视觉颤动。
假如用户在短时间间隔内频繁点击(抢票)表单的提交按钮,我们不做限制, 服务器是否能处理过来?如果对于每次提交后服务端的返回值都影响到客户端的界面呈现,这时客户端还会有 idle 做交互?就算客户端空闲,当用户有其它交互发出请求然后需要用到服务端返回的数据做呈现的时候,服务端是否能及时响应?假如在指定间隔内只执行第一次提交的回调,只发一次提交的请求,这样就能减少服务器的压力和客户端的计算能力消耗。使得页面更加流畅,用户的交互行为能更快得到响应。
适用场景:
到底是要防抖还是要节流?这取决于你想要在用户有操作时候先响应再间隔响应还是直接每间隔一段时间响应最后一次操作。 -
防抖适用场景:滚动、鼠标移动、window resize、防抖动事件、键盘事件keydown keyup
-
节流适用场景: 滚动、鼠标移动、window resize、键盘事件keydown keyup、按钮抢票、秒杀、下拉加载等。
-
3、具体实现不同
-
防抖的实现
//防抖debounce代码:
function debounce(fn,delay) {
var timeout = null; // 创建一个标记用来存放定时器的返回值
return function (e) {
// 每当用户输入的时候把前一个 setTimeout clear 掉
clearTimeout(timeout);
// 然后又创建一个新的 setTimeout, 这样就能保证interval 间隔内如果时间持续触发,就不会执行 fn 函数
timeout = setTimeout(() => {
fn.apply(this, arguments);
}, delay);
};
}
// 处理函数
function handle() {
console.log('防抖:', Math.random());
}
//滚动事件
window.addEventListener('scroll', debounce(handle,500));
- 节流的实现
//节流throttle代码:
function throttle(fn,delay) {
let canRun = true; // 通过闭包保存一个标记
return function () {
// 在函数开头判断标记是否为true,不为true则return
if (!canRun) return;
// 立即设置为false
canRun = false;
// 将外部传入的函数的执行放在setTimeout中
setTimeout(() => {
// 最后在setTimeout执行完毕后再把标记设置为true(关键)表示可以执行下一次循环了。
// 当定时器没有执行的时候标记永远是false,在开头被return掉
fn.apply(this, arguments);
canRun = true;
}, delay);
};
}
function sayHi(e) {
console.log('节流:', e.target.innerWidth, e.target.innerHeight);
}
window.addEventListener('resize', throttle(sayHi,500));