美文网首页
防抖(Debouncing)、节流(Throttling)和 r

防抖(Debouncing)、节流(Throttling)和 r

作者: milletmi | 来源:发表于2018-07-02 14:20 被阅读0次

    网页生成的时候,至少会渲染(Layout+Paint)一次。用户访问的过程中,还会不断重新的重排(reflow)和重绘(repaint)。用户 scroll 和 resize 行为(即是滑动页面和改变窗口大小)会导致页面不断的重新渲染。scroll 事件本身会触发页面的重新渲染,同时 scroll 事件的 handler 又会被高频度的触发, 因此事件的 handler 内部不应该有复杂操作,例如 DOM 操作就不应该放在事件处理中。针对此类高频度触发事件问题(例如页面 scroll ,屏幕 resize,监听用户输入等),下面介绍两种常用的解决方法防抖节流

    感谢博主分享 :
    ChokCoco
    Alon's Blog

    防抖(Debouncing)

    防抖技术即是可以把多个顺序地调用合并成一次,也就是在一定时间内,规定事件被触发的次数。

    window.addEventListener('scroll',debounce(realFunc,500))
         //优化滚动效果防抖动
         function debounce(fn,time){ 
            var timeout = null;
            return function(){
               clearTimeout(timeout)
               timeout = setTimeout(fn,time) 
            }
         }
         function realFunc(){
           console.log('this is real callback function')
         }
               
         }
    

    原理:

    • 首先,我们为scroll事件绑定处理函数,这时debounce函数会立即调用,
      因此给scroll事件绑定的函数实际上是debounce内部返回的函数

    • 每一次事件被触发,都会清除当前的 timer 然后重新设置超时调用。
      这就会导致每一次高频事件都会取消前一次的超时调用,导致事件处理程序不能被触发

    • 只有当高频事件停止,最后一次事件触发的超时调用才能在delay时间后执行
      继续优化的写法:不希望非要等到事件停止触发后才执行,我希望立刻执行函数,然后等到停止触发 n 秒后,才可以重新触发执行。

    window.addEventListener('scroll',debounce(realFunc,500,true))
         //优化滚动效果防抖动
         function debounce(fn,time,immediate){ 
            var timeout = null;
            return function(){
                if(timeout){
                  clearTimeout(timeout)
                }
                if(immediate){
                  //根据距离上次触发操作的时间是否到达delay来决定是否要现在执行函数
                  var doNow = !timeout
                  //每一次都重新设置timer,就是要保证每一次执行的至少delay秒后才可以执行
                  timeout = setTimeout(function(){
                    timeout = null
                  },time)
                  //立即执行
                  if(doNow) fn()
                }else{
                  timeout = setTimeout(fn,time) 
                }
               console.log('after'+timeout)
            }
         }
         function realFunc(){
           console.log('this is real callback function')
         }
               
         }
    

    Debounce 实例 : 调整大小的例子 调整桌面浏览器窗口大小的时候,会触发很多次 resize 事件。
    oninput实时搜索的demo

    节流(Throttling)

    防抖函数确实不错,但是也存在问题,譬如图片的懒加载,我希望在下滑过程中图片不断的被加载出来,而不是只有当我停止下滑时候,图片才被加载出来。又或者下滑时候的数据的 ajax 请求加载也是同理。
    这个时候,我们希望即使页面在不断被滚动,但是滚动 handler 也可以以一定的频率被触发(譬如 250ms 触发一次),这类场景,就要用到另一种技巧,称为节流函数(throttling)。
    节流函数,只允许一个函数在 X 毫秒内执行一次。
    与防抖相比,节流函数最主要的不同在于它保证在 X 毫秒内至少执行一次我们希望触发的事件 handler。
    与防抖相比,节流函数多了一个 mustRun 属性,代表 mustRun 毫秒内,必然会触发一次 handler ,同样是利用定时器,看看简单的示例:

    window.addEventListener('scroll',throttle(realFunc,500,1000))
         //优化滚动效果节流
         function throttle(fn,time){ 
            var timeout,
                startTime = new Date()
            return function(){
               var context = this,
                   args = arguments,
                   curTime = new Date(),
                   remaining = time - (curTime - startTime);
               clearTimeout(timeout)
            if(remaining <=0){
                  fn.apply(context,args)
                  startTime = curTime
               }else{
                   timeout = setTimeout(fn,remaining) 
               }
    
            }
         }
         function realFunc(){
           console.log('this is real callback function')
         }
    

    节流阀实例 : 无限滚动 用户向下滚动无限滚动页面,需要检查滚动位置距底部多远,如果邻近底部了,我们可以发 AJAX 请求获取更多的数据插入到页面中。
    下拉加载demo

    使用 rAF(requestAnimationFrame)触发滚动事件

    上面介绍的抖动与节流实现的方式都是借助了定时器 setTimeout ,但是如果页面只需要兼容高版本浏览器或应用在移动端,又或者页面需要追求高精度的效果,那么可以使用浏览器的原生方法 rAF(requestAnimationFrame)。
    简单而言,使用 requestAnimationFrame 来触发滚动事件,相当于上面的:

    throttle(func, xx, 1000/60) //xx 代表 xx ms内不会重复触发事件 handler
    
    滑动过程中尝试使用 pointer-events: none 禁止鼠标事件/不要去修改样式

    相关文章

      网友评论

          本文标题:防抖(Debouncing)、节流(Throttling)和 r

          本文链接:https://www.haomeiwen.com/subject/pdqluftx.html