最近频繁用到浏览器窗口的resize事件和页面的滚动事件(scroll),我们都知道这些事件会频繁的调用回调函数,如果回调函数里面只是做一些简单的数据处理也就罢了,坏就坏在这里面会进行一些DOM操作或者是发出请求和后台进行交互,频繁的触发导致页面卡顿,影响使用。
这时就想到了大神器函数节流与防抖,可以改善这一情况。至于具体使用哪一个,我们就要来好好分析一下两者的区别和使用场景了。
函数节流与防抖其实都是在事件和函数执行之间加了一个控制层,我们无法控制DOM事件触发的频率,但是可以控制函数执行的频率呀~
函数节流
就像地铁站在高峰期缩小通道控制人流量一样,并不是不让人进了,而是进的慢一些,不然呼啦一下都涌进去了,地铁承受不住啊~~ 事件触发回调函数也一样,不是不执行了,而是执行的频率慢一些,隔一段时间执行一次,而且这段时间只执行一次,所以我们要保存上次执行的时间点和本次执行的时间点进行比较。
function throttle(fn, threshold) {
let startTime = new Date(); // 上次执行时间
let currentTime; // 本次执行时间
let timerId;
threshold = threshold || 200; // 默认200ms
return function(...rest) {
currentTime = new Date();
if (currentTime - startTime >= threshold) { // 若间隔时间大于等于threshhold,则执行方法
fn.apply(this, rest);
startTime = currentTime;
} else { // 清除没执行的事件回调,重新注册一次,保证方法在最后也能执行一次
clearTimeout(timerId);
timerId = setTimeout(() => {
fn.apply(this, rest);
}, threshold)
}
}
}
适用场景:
比较常用于resize、scroll和mousemove等鼠标滑动事件中,throttle会强制函数以固定的速率进行。适合用于动画相关的场景。
函数防抖
这个我们可以结合电梯的例子理解一下,如果有人一直按电梯的话,电梯是不会走的,只有最后没人按了,电梯才会走。函数防抖就是把多次函数调用合并成一次,在用户操作完之后调用。
怎么保证操作完之后调用呢?我们把函数放在定时器里面,每次调用先清除定时器,这样就能保证在操作的时候不会触发定时器了。
function debounce(func, delay) {
return function(...rest) {
clearTimeout(func.timerId);
func.timerId = setTimeout(() => {
func.apply(this, rest)
}, delay);
}
}
适用场景
- input输入验证或者根据输入内容向后台请求数据的时候
- 点击确定按钮提交结果的时候
requestAnimationFrame
看lodash实现的时候发现了这个方法,就查了一下它的用法。
window.requestAnimationFrame() 方法告诉浏览器您希望执行动画并请求浏览器在下一次重绘之前调用指定的函数来更新动画。 --- MDN
RAF常用于动画的制作,用于准确控制页面的帧刷新渲染,让动画更加流畅。RAF被调用的频率是每秒60次,大概是16.7ms/次,我们也可以利用它来实现节流的效果:
throttle(fn,16.7)
不同浏览器支持requestAnimationFrame前缀不同:
window.requestAnimFrame =(function(callback){return window.requestAnimationFrame ||window.webkitRequestAnimationFrame ||window.mozRequestAnimationFrame ||window.oRequestAnimationFrame ||window.msRequestAnimationFrame ||function(callback){window.setTimeout(callback,1000/60);};})();
IE 浏览器IE10以上版本支持