网页性能优化-防抖

344 阅读2分钟

场景

有一个输入框,需要根据输入内容去更新数据,这里有个问题若是用 change,那得失去焦点才能拿到数据,若是用 keyup 又会每次按一个键都更新。遇到类似这种问题我们就可以用防抖来解决了,只要用户还在输入,就不执行更新数据的操作,当用户停止操作时间 n 之后再去做处理,这样就可以达到一个输入优化的效果了。

实现

先来看一个最简单的实现方法

function debounce(func, wait) {
  var timeout;
  return function () {
    clearTimeout(timeout)
    timeout = setTimeout(func, wait);
  }
}

上述 demo 利用闭包的特性,把 timeoutId 存在了上层作用域的 timout 变量里,每次处理返回的函数都拿得到这个 timeoutId,从而可以做到取消定时的功能。按照上面的描述,我们需要做到最后一次输入时间n之后才去执行处理数据的程序,所以这里用了定时器,延迟程序的执行,每次输入都取消掉上一次的定时任务,所以定时任务只会在最后一次输入后时间n才会执行。

接下来有个问题,我们的函数需要拿到一些参数,根据参数去更新数据,上述 demo 是无法拿到参数的,这里我们可以使用 apply 去实现,接下来看一下带参数的 demo

function debounce(func, wait) {
  var timeout;
  return function () {
    var context = this
    var args = arguments;
    clearTimeout(timeout)
    timeout = setTimeout(()=>{
      func.apply(this,args)
    }, wait);
  }
}

至此,上述功能基本实现了,但是要输入完后等时间n才能执行,能不能先执行程序,间隔时间 n 再触发下一次执行呢?再改进一下 demo

function debounce(func, wait, immediate) {
  var timeout, result;

  return function () {
    var context = this;
    var args = arguments;

    if (timeout) clearTimeout(timeout);
    if (immediate) {
      // 如果已经执行过,不再执行
      var callNow = !timeout;
      timeout = setTimeout(function () {
        timeout = null;
      }, wait);
      if (callNow) result = func.apply(context, args);
    } else {
      timeout = setTimeout(function () {
        func.apply(context, args);
      }, wait);
    }
    return result;
  };
}

完整 demo:

See the Pen debounce by Anna (@AnnaLoveLife) on CodePen.