JavaScript 事件节流和事件防抖

4,415 阅读4分钟

一个网站,想要吸引更多的流量,提高用户粘性,除了良好的设计和优雅的交互外,最重要的就是加载速度了。没有用户愿意等,哪怕是1秒,他们都可能不再来了。这个时候性能优化就显得十分重要了。

性能优化的方案中不得不提的是雅虎军规,列表中总结了很多值得大家注意的地方和解决方案,可供大家参考。当然雅虎军规并没有覆盖所有的性能优化方案,这里我们需要提一下性能优化中的事件优化---事件节流(throttle)与事件防抖(debounce),这两点在实际应用的十分普遍

背景

假如我们为DOM绑定了onScroll事件,当使用触控板,滚动滚轮,或者拖拽滚动条的时候,一秒可以轻松触发几十次事件。更加夸张的是手机上的表现:慢慢滑动一下,一秒可以触发事件100次之多。这么高的执行频率,你的回调函数压力大可想而知,更加蛋疼的是:函数触发频率过高可能会导致的响应速度跟不上触发频率,出现延迟,假死或卡顿的现象。那这种就是非常差的体验。不要以为这个是小问题,当年,鼎鼎大名的Twitter网站就出现了这个问题:向下滚动 Twitter 信息流的时候,变得很慢,很迟钝。就是因为 scroll 事件关联了开销大的函数导致的。

加上了防抖和节流的函数变得特别有用,why?因为我们在事件和函数执行之间加了一个控制层,这个控制层控制的是函数是否执行,注意我们不能去控制 DOM 事件触发频率。

介绍完背景,我们来分别说一下事件节流和事件防抖

共同点:都限制函数的执行频次,达到优化的目的

不同点

  • 事件节流

    处理方法是只允许一个函数在 x 毫秒内执行一次,跟 防抖 主要的不同在于,节流 保证 x 毫秒内至少执行一次。举一个最近简单的🌰: 每200ms检查下滚动位置,并触发页面加载

  • 事件防抖

    处理方法是把触发非常频繁的事件(比如key event ,resize)合并成一次执行

说了这么多,都是概念上的东西,估计大家烦了,马上进入大家喜欢的环节,荷枪实弹的来一发(你也可以自己造一个 debounce/throttle 的轮子或者随便找个博文复制过来。我这里是直接使用 lodash(还可以用underscore) 。使用 lodash 自定义构建工具,生成一个 仅包含 _.debounce 或 _.throttle 方法的压缩库,,可以使用以下的简单命令即可:

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

好了,先献上事件节流的🌰,供大家参看

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
      body {
        background: #e1e1e1;
        margin:0 auto;
        max-width:600px;
        padding:12px;
      }
      .item {
        height:120px;
        width:100%;
        margin-top:50px;
        padding:20px;
      }
      .color-1 { background: #9BFFBB}
      .color-2 { background: #B9C6FF}
      .color-3 { background: #FFA3D8}
      .color-4 { background: #FF8E9B}
    </style>
</head>
<body>
<h1>这是一个事件节流的🌰 呵呵哒</h1>
<div class="item color-1">Block 1</div>
<div class="item color-2">Block 2</div>
<div class="item color-3">Block 3</div>
<div class="item color-4">Block 4</div>
</body>
<script src="https://cdn.bootcss.com/jquery/2.1.0/jquery.min.js"></script>
<script src='http://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.5.0/lodash.min.js'></script>
<script>
    $(document).ready(function(){
    // Check every 200ms the scroll position
    $(document).on('scroll', _.throttle(function(){
      check_if_load_more();
    }, 300));

    function check_if_load_more() {
      BottomPixelsFromWindow = 0 + $(document).height() - $(window).scrollTop() -$(window).height();
      if (BottomPixelsFromWindow < 200){
        $('body').append($('.item').clone());
      }
    }
  });
</script>
</html>

再来个事件防抖的,巩固一下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
      body {
        background: #e1e1e1;
        color: white;
        margin:0 auto;
      }
      .left-part,
      .right-part {
        padding:1%;
        margin:0;
        width:45%;
        float:left;
        min-height:100vh;
        text-align:center;
      }

      .left-part {
        background: #999999;
        color:black;
      }
    </style>
</head>
<body>
<h1>这是一个事件防抖的🌰 呵呵哒</h1>
<div class="left-part">normal resize events<br></div>
<div class="right-part">Debounced resize events<br></div>
</body>
<script src='http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js'></script>
<script src='http://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.5.0/lodash.min.js'></script>
<script>
  $(document).ready(function(){
    var win = $(window);
    var left_part = $('.left-part');
    var right_part = $('.right-part');

    function print_info(div) {
      div.append(win.width() + ' x ' + win.height() +  '<br>');
    }

    $(window).on('resize', function(){
      print_info(left_part);
    });

    $(window).on('resize', _.debounce(function() {
      print_info(right_part);
    }, 400));
  });
</script>
</html>

说了这么多,事件节流和事件防抖真的用处很大 如果记不住,你也可以比喻着想:

事件节流像是班车:固定在一段时间后执行,不会等乘客(事件),到点发车

事件防抖像是黑车:说是再等五分钟就走,但是如果又有人上车了(在5分钟内再次触发事件),那你又得等5分钟才能再出发(重新计时)

不恰当的🌰哈 ,帮助记忆

未经本人允许,不得转载,文章有疏漏浅薄之处,请各位大神斧正