![02affdb18df8fdf089286cd174fe6b2f.png](https://i-blog.csdnimg.cn/blog_migrate/8bc2d41daa86827622dba5e7bf8e4ba9.jpeg)
【性能优化】0202年了,函数节流与防抖还不用起来?
引言
还有一个多月就要过年了。温馨提示:小伙伴们该抢票了。
说到过年,不得不提的就是春运。我们可以从春运中,体验一下节流和防抖思想给社会带来的秩序和效率上的提升。
场景一:安检
地铁过安检,安检员会在入口控制人流。当人流过多时,会及时对后面的人流阻断,起到节流的作用,避免过于拥挤而阻塞安检通过,进一步提高过检效率。
场景二: 拥挤的小巴车。
小巴车不知道大家有没有体验过。 如果陆陆续续有乘客上车,售票员一般是不会轻易发车的。他心里大概会想,等等,再等等,10分钟内还没有乘客,就发车。此时,已上车的乘客心里苦啊。。。当然从运输效率上讲,这种典型的防抖思想的应用,大大提升了运输效率,避免了运输资源的浪费。
原理分析
函数节流和防抖的概念已经老生常谈了。有很多文章都讲到过。如果还不太了解,没关系。本文希望能给到大家比较舒适的理解体验。
假设
事件处理函数为 A;
节流(或防抖)函数为 B;
则,最终事件触发时,实际执行的是B(A)返回的内部函数(闭包)。
节流(Throttle)函数: 对于持续的事件触发,每达到固定时间间隔,执行事件处理函数A。
![0368b497f9de3ad9b6c5502d7dde245a.png](https://i-blog.csdnimg.cn/blog_migrate/8d8584dfad68eead634b8276b67a9334.jpeg)
防抖(Debounce)函数: 事件触发停止后开始计时,在固定时间内不再有事件触发,执行事件处理函数A。
![02c4bee1b11db0d070dc9e0446711475.png](https://i-blog.csdnimg.cn/blog_migrate/d01aeca1a49193968cfdf85ce80190c0.jpeg)
后边,我们会结合具体示例,加强理解。
重要作用
在讲具体应用前,希望大家先问自己几个问题?
- 为什么会有
节流
和防抖
的概念,他们是为了解决什么样的问题? - 为了提高代码逼格,日常开发中要不要用起来?
- ……
看完本文希望大家能得到答案。
作为一名与用户”零距离“的前端开发人员,用户体验这块我们应该拿的sisi的。节流和防抖,就是一种”讨好“用户的重要手段。
① 优化用户体验(适时反馈,避免UI渲染阻塞,浏览器卡顿);
② 提升页面性能(避免页面渲染卡顿,减少服务器压力,防范恶意触发);
日常业务开发中,页面中连续触发的事件处理,一般需要使用函数节流和防抖来进行优化。例如: 页面滚动(scroll)、改变页面尺寸(resize)、表单验证(input)、按钮点击(click)、 鼠标滑动(mousemove)等。接下来,我们一起来看看具体实现和应用场景。
应用场景
节流
实现
/**
* 节流函数
* @param {Function} fn
* @param {Number} delay
*/
function throttle(fn, delay) {
var last = 0;
return function () {
var now = Date.now();
if (now - last >= delay) {
fn.apply(this, arguments);
last = now;
}
}
}
应用
假设一个抢优惠劵的业务场景:用户连续点击优惠劵,每点击一次就会得到一张优惠券,最多10张,总量1000张(有限)。双十一刚过,阿里电商2000多亿的现金流量,想必消费者优惠券都抢的够够的。为了抢到更多优惠券,用户常规操作应该是,手动持续点击。而非常规操作,可能会写一个for循环,把这10张券轻轻松松搞到手。
for循环抢券,如下图:
![01dfce75394b5f3bb377d087ed1e79ff.gif](https://i-blog.csdnimg.cn/blog_migrate/ecb19bcab9415fa15bf33af0ae5f75ab.gif)
上图显示,执行了1000次抢优惠券逻辑,是不是很暴力!假如每个用户没有最多领10次的限制,1000张优惠券,瞬间会被恶意抢走。全网常规操作的用户,手再快,也不可能胜过一个for循环。 ……
当然,
严谨的抢券响应逻辑,一定不会让这样的事情发生。节流防范了解一下。
// 优惠劵点击,事件处理函数
function snatchCouponFn() {
console.log('恭喜您,优惠券已到手');
}
// 使用 节流函数(已给出实现,往上找找)包装抢优惠劵的事件处理函数
$coupon.onclick = throttle(snatchCouponFn, 2000)
再来看看效果:
![40ca072508b40e11280d4dd17ee3099b.gif](https://i-blog.csdnimg.cn/blog_migrate/f8b39fc8d0a750d6dc3d9109e2630386.gif)
最后仅执行了1次抢优惠券逻辑。✌️
通过上边的例子,函数节流不仅提升了页面性能,而且保证了业务程序的安全性。
经验有限,更多方面的应用,就不一一举例了。欢迎大家在评论区留下你的经验,帮助更多的人把节流的思想用起来。
防抖
实现
/**
* 防抖函数
* @param {Function} fn
* @param {Number} delay
*/
function debounce(fn, delay) {
var timer;
return function() {
console.log('事件触发');
var self = this,
argumentsBySelf = arguments;
clearTimeout(timer);
timer = setTimeout(function() {
fn.apply(self, argumentsBySelf);
}, delay);
}
}
应用
常见场景:在填写表单时,检查表单值是否有效。
![548991bbaf8bf8e7308c5d463363136d.gif](https://i-blog.csdnimg.cn/blog_migrate/551fc4fa02f2e6f94b2d0aa11196ff75.gif)
来看一下我们添加防抖的过程:
//伪代码
function onInputFn() {
$error.innerHTML = '昵称重复,请换用其他昵称';
console.log('事件响应');
}
$name.oninput = debounce(onInputFn, 2000);
依据debounce的实现原理,当表单input连续输入时,内部函数会被连续触发。执行的操作是,清除上一次的定时器 clearTimeout(timer)
,同时重新设定一个定时器,这样上一次定时器从不会执行,fn
就不会被执行到。但当用户停止向表单继续输入时,达到debounce
调用时设定的时间 delay ,最后一次设定的定时器会被执行,fn
最终被执行,来完成表单值校验操作。
当然,我们常用的方法还有onblur(当表单项失去焦点时触发)来执行表单值校验。但这样不如oninput+debounce
的方案给到用户反馈及时和体验好。
类似校验昵称是否重复这类需求,一般是需要调用后端接口,使用debounce对校验函数做包装后,相比与单纯使用oninput
, 可大大减少后端接口的调用次数。
不足
经过上面分别对throrrle和debounce的示例介绍,大家也应该发现了他们各自的小瑕疵:
- 经过Throttle 处理的函数,在用户刚停止触发事件时,很可能,不再会对用户的操作做出任何反馈;
- 经过Debounce处理的函数,在用户刚开始触发事件时,也并没有给到用户任何的反馈。
但这在很多场景下,可能会导致不良甚至错误的用户体验。所以,能不能在用户刚开始触发
时或结束触发时
或是长时间触发
过程中也能给到用户必要的反馈(事件响应)呢 ? 答案是,可以的。来看一下实现。
/**
* 加强版——防抖
* @param {Function} fn
* @param {Number} delay
*/
function enhancedDebounce(fn, delay) {
var timer,
last = 0;
return function() {
var self = this,
argumentsBySelf = arguments,
now = Date.now();
if(now - last < delay) {
clearTimeout(timer);
timer = setTimout(function() {
fn.apply(self, argumentsBySelf);
}, delay)
} else {
fn.apply(this, arguments);
last = now;
}
}
}
应用
…… 笔者后续作补充。欢迎大家评论区留言,分享你的应用故事。
小结
都说细节决定成败。本文是在讨论日常开发中,关于性能优化的小细节,希望大家都能善用到自己code中去。小细节,大作用。
See you, next time ~