debounce 函数有什么用? 如何在 react 中使用 debounce?

作者: 科技微讯

日期:

这篇文章主要分享以下内容: 什么是 debounce 函数? debounce 函数有什么用? 如何写一个 debounce 函数? 如何在 react 使用 debounce 函数?

关于 debounce 函数

我喜欢顾名思义, bounce 有跳动&反弹等意思, debounce 就是使之不跳动, debounce 函数可以理解为使一个函数不那么活跃, 具体地说, 就是减少一个函数的执行次数.

比如在一个 <input> 元素中绑定了一个叫 onInput 的事件, 当我们把光标定位到输入框, 每一次按下键盘, 这个 onInput 事件都会被触发, 事件对应的函数都会被执行一次.

事件函数每一次执行可能都要花费很长时间, 例如根据你输入的内容在海量数据中进行查询, 比如你要查询的是 'abc' 这个关键词, 但事实上 onInput 事件会相继地被 'a', 'ab', 'abc' 这三个关键词所触发, 而我们并不需要 'a' 和 'ab' 的查询结果, 于是就产生了资源浪费, 甚至可能会拖慢系统速度.

比如下面这个例子, 每一次按下键盘都会触发 onInput 事件对应的函数:

再比如 <input type="range" onChange="onChangeRange"/>, 我们拖动滑块滑动的过程中 onChangeRange 函数会被不断触发, 触发频率可能高达几十次/秒, 如果 onChangeRange 函数运算复杂, 就可能会影响系统的运行速度, 如果我们不关心拖动滑块过程中的事件, 只关心拖动结束后的事件, 那就可以对 onChangeRange 进行 debounce, 让它不那么活跃, 减少它的执行次数.

debounce 的作用如下图所示, 可以看到快速输入过程中事件函数中的关键运算并没有被执行, 只有当停止输入 250ms 之后才执行.

如何写一个 debounce 函数

debounce 函数接收一个需要被 debounce 的事件函数以及一个时间作为参数, 最后返回一个新的函数, 其简单的写法可见下方代码.

var timeout = null 这行是关键, 在上面这个例子中, 用户每按一次键盘, 都会产生一个新的 timeout ID, 这个 ID 都保存在 timeout 这个变量中, 旧的 ID 不断地被 clear, 新的 ID 不断地被赋值给 timeout, 当用户停止输入时, 最新的 ID 才不会被 clear, 于是就在一定时间之后执行事件函数.

function App() {
  // 这是 debounce 函数
  const debounce = function(f, wait) {
    var timeout = null
    return function() {
      clearTimeout(timeout)
      timeout = setTimeout(() => {
        timeout = null
        f.apply(this, arguments)
      }, wait)
    }
  }
  // 这是事件函数, 即需要被 debounce 的函数
  const inputHandler = function(e) {
    console.log(e.target.value)
  }
  // 通过 debounce 函数对事件函数进行 debounce
  const debounceInput = useCallback(debounce(inputHandler, 250), [])
  // react 需要这样把 e 传递给被 debounce 的事件函数, 即传递给 debounceInput
  const onInput = e => {
    e.persist()
    debounceInput(e)
  }
  return (
    <div>
      <label htmlFor="input">输入文字:</label>
      <input id="input" type="text" onInput={onInput} />
    </div>
  )
}

在 react 中使用 debounce 的方法是:

  • 按照以往的思路写 event handler;
  • 把这个 handler 传入 debounce 函数获得一个被 debounce 的函数;
  • 把 event 对象通过一个新的 event handler 传入被 debounce 的函数;

所以从 bounce 切换到 debounce, 基本不需要删除或更改已有的代码, 主要是增加一个 debounce 函数. 这个 debounce 函数可以自己定义, 如上方例子, 但建议用 lodash 版本, 它可接收更多参数, 有更多选项..

首先安装:

npm i --save lodash.debounce

然后在 react 中引入:

import debounce from "lodash.debounce"

参考资料: Debouncing and Throttling Explained Through Examples