前端性能优化之函数防抖与截流

362 阅读3分钟

在前端开发当中,我们都知道有些交互事件,会频繁触发。这样会导致我们的页面渲染性能,如果频繁触发接口调用的话,会直接导致服务器性能的浪费。话不多说,盘它!

我们先简单列一下

onresize onmousemove onkeydown srcoll onkeyup

这里我是用键盘事件 keyup作为测试

我们先看一段测试用例

<ul>
  <li>
    未做处理:<input type="text" id="ipt">
    <div>函数被调用:<span id="count">0</span>次</div>
  </li>
</ul>
const oCount = document.getElementById('count');
const oIpt = document.getElementById('ipt');
let init = 0
oIpt.onkeyup = function() { 
  oCount.innerText = ++init
}

看一下输入时候的运行效果。

我们不难发现,每次输入都会触发事件的执行。如果我们用这样的方式去检测:当前用户输入的用户名是否可用?如此高频率的触发不仅是极大的浪费,而且用户还没有输入完成就检测,对用户的提示也不好。应该等用户输入完了,我们再去触发函数,下面我们优化一下:

// 设置一个默认值 300ms
const oCount2 = document.getElementById('count2');
const oIpt2 = document.getElementById('ipt2');

// 设置一个默认值 300ms
const debounce = (fn, wait = 300) => {
  let time = null
  return function(arguments) {
    const _this = this, args = arguments
    clearTimeout(time)
    time = setTimeout(() => {
      fn.apply(_this, [args])
      // fn.call(_this, args)   //或者使用这个
    }, wait)
  }
}

let init2 = 0
oIpt2.onkeyup = debounce(function() {
  oCount2.innerText = ++init2
}, 500)  //可以自己定义延迟时间间隔

再来看一下比对效果:

可以看到,加了防抖函数之后,当我们在频繁输入的时候,函数并没有执行, 只有在函数指定的间隔内(500ms)不再输入了,才会执行函数。如果在时间间隔之内继续输入,会触发函数重新计数。

函数防抖的概念:在事件触发后的n秒之后,再去执行真正需要执行的函数,如果在这n秒之内事件又被触发,则重新开始计时

也就是说,如果用户在间隔时间内一直触发函数,那么这个防抖函数内部的真正需要执行的函数将永远无法执行。

那有没有什么好点的办法,让用户在输入过程中,既能触发真实需要的函数,又能达到优化的效果?答案是肯定的,那就是:

函数截流

事例代码如下

const oCount3 = document.getElementById('count3');
const oIpt3 = document.getElementById('ipt3');
const oTime = document.getElementById('time');

const throttle = (fn, threshhold = 1000) => {
  let last, deferTimer
  return function(arguments) {
    const _this = this, args = arguments
    let now = +new Date()
    if(last && now < last + threshhold) {
      clearTimeout(deferTimer)
      deferTimer = setTimeout(function () {
          last = now
          fn.call(_this, args)
        }, threshhold)
    } else {
       last = now
         fn.call(_this, args)
    }
  }
}
let init3 = 0
const throttleIpt = throttle(function() {
  let time = new Date().getMinutes() + ':' + new Date().getSeconds()
  oTime.innerText = time
  oCount3.innerText = ++init3
}, 1000)  // 初始化一下

oIpt3.onkeyup = function() {
  throttleIpt()
}

由于我们设置的时间间隔是1000ms也就是1s。我来看一下优化效果:(注意看调用次数和时间上秒的变化)

可以看到,任由我们怎么输入,函数会按照我们设定的时间(1s),每秒执行一次。你可以设置更大。

函数截流的概念:规定好一个单位时间,触发函数一次。如果在这个单位时间内触发多次函数的话,只有一次是可被执行的。想执行多次的话,只能等到下一个周期里

他们之间的优缺点,等我下次总结吧!