Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

防抖节流 #1

Open
zxw018018 opened this issue Aug 15, 2018 · 0 comments
Open

防抖节流 #1

zxw018018 opened this issue Aug 15, 2018 · 0 comments

Comments

@zxw018018
Copy link
Owner

zxw018018 commented Aug 15, 2018

防抖节流

防抖 debounce

bounce是弹跳、抖动的意思。
debounce就是去抖动、防反跳。
这个术语的词源来自于电子工程。当机械开关从断开到闭合时,开关中的弹簧会导致开关的接触不良,从而导致出现数次断续的通断现象。
下图就是一个典型的开关抖动电压图:

debounce_circuit

从图中可以看到,开关由断开到闭合的切换过程中,由于机械弹簧的不稳定性,造成了电压的不稳定,萎了消除这种现象,我们就要去抖动,debounce这个概念应运而生。我们只希望捕获最后那一次精准的状态切换,忽略之前的连续切换抖动。
此后,debounce被引申到软件工程中:

Debouncing enforces that a function not be called again until a certain amount of time has passed without it being called.
For example, “execute this function only if 100 milliseconds have passed without it being called.”

防抖的作用是:一段时间内,函数没有被再次调用,它才会被触发。比如,“只有当这个函数100ms内没有被调用才会执行它”。
也就是说,防抖技术允许我们把多个连续的函数调用“组合”成一个函数调用。只有最后一次非连续的操作才会被触发。

典型应用场景

用户输入要搜索的文字触发搜索联想,调用后台接口返回数据,前端页面列表就会刷新显示。
如果每输入一个字符就立刻触发后台搜索,就会造成性能浪费和界面卡顿。
比如,下图我们使用google搜索"vue":

google_search

我们不希望输入"v"或"vu"时就调用后端接口,触发搜索联想,我们希望"vue"全输完之后才触发搜索联想。于是,我们就需要设置一个时间间隔,比如200ms,使用debounce函数,在200ms内如果keyup事件被触发,就不会触发搜索联想,如果没有触发keyup事件,才会触发搜索联想。

google_search_debounce

我们打开控制台可以看到确实是这样:"v"和"vu"试图调用后台搜索联想的请求被取消了,只有"vue"的搜索联想被成功触发了。

debounce实现

// 只要它还在继续被调用,它就不会被触发
function debounce (func, interval) {
  var timeout;
  return function () {
    var context = this, args = arguments;
    var later = function () {
      timeout = null;
      func.apply(context, args);
    };
    clearTimeout(timeout);
    timeout = setTimeout(later, interval);
  }
}

可以看到,每次它继续被调用时,之前的timeout就会被clear,timeout会被重置。只有当它不再被调用了,才会成功在interval后触发这个function。

节流 throttle

throttle这个术语的词源来自机械工程。它是节流阀的意思,也就是我们俗称的油门,它用来调整空气进入汽油引擎的量,从而控制引擎做功来控制汽车速度。
引申到软件工程中就是控制函数被触发的频率。

Throttling enforces a maximum number of times a function can be called over time.
For example, “execute this function at most once every 100 milliseconds.”

节流的作用是:迫使函数在一段事件内只能被调用某个最大次数。比如,“每100ms至多执行这个函数一次”。
也就是说,使用节流技术,每X毫秒内我们不会允许函数执行超过一次。在一定时间内只执行一次,多次的操作会被过滤。

典型应用场景

在画布上做一个鼠标相关的应用,如果不做过滤处理, 每移动一个像素都可能触发一次mousemove事件,这样频繁的调用会造成很糟糕的用户体验,所以可以用throttle过滤一下。比如,每50ms才会去看看鼠标有没有移动。

throttle实现

// 只要它还在被继续调用,它就会在每个时间间隔被触发
function throttle (func, interval) {
  var timeout;
  return function() {
    var context = this, args = arguments;
    var later = function () {
      timeout = false;
    };
    if (!timeout) {
      func.apply(context, args)
      timeout = true;
      setTimeout(later, interval);
    } 
  }
}

可以看到,如果在interval内,timeout为true,不会触发这个function。只有当过了这个interval之后,才会触发。

区分

  • 按一个按钮发送Ajax:给click加了debounce后就算用户不停地点这个按钮,也只会最终发送一次;如果是throttle就会间隔发送几次。
  • 监听滚动事件判断是否到页面底部自动加载更多:给scroll加了debounce后,只有用户停止滚动后,才会判断是否到了页面底部;如果是throttle的话,只要页面滚动就会间隔一段时间判断一次。

应用

在使用Element UI的过程中,使用到了表格筛选器这一组件,如下所示:
filter-component
这种筛选功能是通过绑定filter-method function实现的

:filters="[{text: '北京', value: 1}, {text: '南京',value: 2}]" 
:filter-method="filterHandler"
filterHandler(value, row, column) {
    const property = column['property'];
    return row[property] === value;
}

可以看到,这种filter-method的本质是对表格数据的每一行都进行判断,和筛选条件进行匹配,如果有一行符合筛选条件,就显示那一行,反之不显示。
所以,使用这种筛选器,表格有多少行,就会调用filter-method多少次,这是一种前端的筛选手段。
假设我想要调用后端接口来实现筛选功能的话,这种筛选显然就不符合要求,如果一页表格上的数据有10行,就会调用10次filter-method,会调用10次接口,这显然很不合理。
为了解决这个问题,我们可以使用节流,让接口只被触发一次。

import _ from 'lodash'
filterHandler: _.throttle(function(value, row, column){
console.log("调用后端接口")
}, 1000, {leading: true, trailing: false})

这样,后端接口在一秒内只会被触发一次。leading和trailing分别对应time interval的前缘和后缘,这里,我只在1秒的前缘触发后端接口。

参考资料

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant