防抖、节流和rAF的区别及使用方法

Debouncing Throttling and RequestAnimationFrame

什么是防抖、节流?

防抖节流是两个十分相似但不同的概念,它们都用于控制一段时间内函数调用的次数。

当我们的函数对DOM事件响应时,防抖节流效果更加明显。需要注意的是,防抖节流并没有控制DOM事件的触发频率,他们只是在事件发生与函数被调用这个过程中充当了一个缓冲层。

为什么需要防抖?

以常见的sroll(滚动)事件为例:

https://codepen.io/dcorb/pen/PZOZgB

执行上述代码,我们会发现滚动事件的触发频率非常高,可能只是轻轻拖动了下滚动条就触发了20次事件。事实上在手机上,滚动事件触发的频率会更高,这么高频事件触发无疑大大增加了事件处理函数的压力。

在这里插入图片描述

仔细聊聊防抖:

用一句话概况:防抖允许我们将一组同一函数的触发序列合并为一次触发,即多化一

The Debounce technique allow us to “group” multiple sequential calls in a single one.

在这里插入图片描述

观察上图,比较原生事件触发与带防抖的事件触发机制。需要注意的是即使事件并不是连续触发的,也会被合并为一次触发(当然不能间隔太久,这个gap可以自定,后面会说到)。

有一个较为形象的类比:想象你现在处在一个电梯中,就在电梯门即将关闭的时候,一个人进来了,电梯不得不延迟升降,打开门让它进来,然后快关闭时又来了一个人,电梯门又开了……虽然这样推迟了电梯的升降,但是却让电梯的空间得以被充分利用。

https://codepen.io/dcorb/pen/KVxGqN

打开这个链接试着在Trigger area中拖动鼠标,你能清晰地看到,防抖通过合并,大大减少了事件触发的次数。(这里强调了合并,因为就像一开始说的那样,防抖并没有改变事件触发的频率,它只是帮我们整理了一下)

前沿(immediate)触发

我们发现上面的防抖总在事件停止“抽搐”后才触发函数,这样有时对体验影响很大,为什么不能让函数立即被触发呢?就好像没有使用防抖时的那样。事实上,在underscore.js中,这个选项被称为immediate。下面是前沿触发防抖的例子:

https://codepen.io/dcorb/pen/GZWqNV

对比之前的可以发现,函数的触发处在序列的前沿了。

在这里插入图片描述

防抖实现

防抖这个概念最早在2009年由John Hann在他的文章中提出:

http://unscriptable.com/2009/03/20/debouncing-javascript-methods/

目前underscore.jsLodash中都有实现防抖,虽然它们具体实现的方式不同,但是接口无疑都是一样的。

Lodash防抖节流增加了更多的细节,如上文提及的immediate选项已经被替换为leading(前沿)和 trailing(后沿)供程序员选择,默认情况下采用后沿触发防抖

防抖实例

改变窗口大小

当我们调整窗口大小时,不可避免地会触发大量调整事件,此时采用后沿触发防抖,因为我们只在意窗口最后地大小,我们并不在意中途发生了什么。

https://codepen.io/dcorb/pen/XXPjpd

在这里插入图片描述

带有自动提交AJAX请求的输入框中

通过防抖避免用户在未输入内容前触发AJAX请求,在这里我们仍采用后沿触发防抖

https://codepen.io/dcorb/pen/mVGVOL

一个相似的案例是用户在注册账号时,通过防抖来提示用户什么时候达到了最短密码长度

节流

用一句话概况:使用节流我们可以保证函数在一段时间内执行次数不超过一次

一个形象的类比是机场的安检系统,它一段时间只允许一个人去安检,无论有多少人隔一段时间只准一个人通过安检。

防抖相似,我们也可以在Lodashunderscore.js中找到节流的实现。

节流实例

无限下拉

这是一个非常常见的案例,在很多网页中都有使用,想要实现无限下拉需要在用户下拉时检测距离底部还有多远,如果接近底部了,就发送一个AJAX请求获取更多数据来填充网页。

在这个案例中防抖技术就显得不那么合适了,我们显然不希望用户不断下拉导致页面始终不更新的情况发生。使用节流我们确保不断地检查距底部的长度,而由不会造成太大负载。

https://codepen.io/dcorb/pen/eJLMxa

如何使用防抖、节流以及一些小坑

相较于自己手写防抖节流或者从其他blog上复制过来,更推荐使用Lodash中现成的实现,引入模块的方式如下:

npm i -g lodash-cli
lodash include=debounce,throttle

注意空格。

一个常犯的使用错误就是多次调用防抖

//错误调用
$(window).on('scroll',function(){
_.debounce(doSomething,300);
})
//正确调用
$(window).on('scroll',_.debounce(doSomething,200))

requestAnimationFrame(请求动画帧)

let cancel=window.requestAnimationFrame(callback);//callback为下次重绘前更新动画帧所用函数
window.cancelAnimationFrame(cancel)//取消回调函数

rAF是另一种限制函数调用速度的方式,它有点类似_.throttle(doSomething,16)但是它具有更高的保真性,因为它是浏览器为了高准确性而提供的原生API。我们可以使用rAF去替代throttle,替换时考虑以下优缺点:

优势:

  • rAF表现为60fps,但它的内部会制定最优的渲染策略。
  • 简单地使用标准的API,不用担心后续的维护问题。

劣势:

  • rAF的开始\取消需要人为控制,不像防抖节流那样内部实现好。
  • 如果浏览器标签没用运作,那么rAF也不会运作。
  • 需要考虑低版本浏览器的兼容问题。
  • rAF不支持node.js

按照经验来看,rAF适用于涉及重计算元素位置的绝大部分情况,如绘画或对动画元素直接操作。对于处理AJAX请求或者增删某个类(涉及css动画)时使用防抖节流效果更好,因为你可以达到60fps以上。

需要注意的是,Lodashunderscore都不包含rAF,因为在浏览器直接调用就已经足够了。

总结:

  • 防抖:允许我们将一组同一函数的触发序列合并为一次触发,即多化一。
  • 节流:保证函数在一段时间内执行次数不超过一次。
  • 请求动画帧:节流的替代品,当你的js涉及计算及重渲染时效果更好。

参考文章:

https://css-tricks.com/debouncing-throttling-explained-examples/#resize-example

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值