在 Vue 中使用lodash对事件进行防抖和节流

点赞再看,养成习惯

本文 GitHub https://github.com/qq44924588... 上已经收录,更多往期高赞文章的分类,也整理了不少个人文档,和教程资料。欢迎Star和完善,你们面试能够参照考点复习,但愿咱们一块儿有点东西。javascript

有些浏览器事件能够在短期内快速触发屡次,好比调整窗口大小或向下滚动页面。例如,监听页面窗口滚动事件,而且用户持续快速地向下滚动页面,那么滚动事件可能在 3 秒内触发数千次,这可能会致使一些严重的性能问题。css

若是在面试中讨论构建应用程序,出现滚动、窗口大小调整或按下键等事件请务必说起 防抖(Debouncing) 和 函数节流(Throttling)来提高页面速度和性能。这两兄弟的本质都是以闭包的形式存在。经过对事件对应的回调函数进行包裹、以自由变量的形式缓存时间信息,最后用 setTimeout 来控制事件的触发频率。前端

Throttle: 第一我的说了算

throttle 的中心思想在于:在某段时间内,无论你触发了多少次回调,我都只认第一次,并在计时结束时给予响应。java

先给你们讲个小故事:如今有一个旅客刚下了飞机,须要用车,因而打电话叫了该机场惟一的一辆机场大巴来接。司机开到机场,心想来都来了,多接几我的一块儿走吧,这样这趟才跑得值——我等个十分钟看看。因而司机一边打开了计时器,一边招呼后面的客人陆陆续续上车。在这十分钟内,后面下飞机的乘客都只能乘这一辆大巴,十分钟过去后,无论后面还有多少没挤上车的乘客,这班车都必须发走。git

在这个故事里,“司机” 就是咱们的节流阀,他控制发车的时机;“乘客”就是由于咱们频繁操做事件而不断涌入的回调任务,它须要接受“司机”的安排;而“计时器”,就是咱们上文提到的以自由变量形式存在的时间信息,它是“司机”决定发车的依据;最后“发车”这个动做,就对应到回调函数的执行。github

总结下来,所谓的“节流”,是经过在一段时间内无视后来产生的回调请求来实现的。只要一位客人叫了车,司机就会为他开启计时器,必定的时间内,后面须要乘车的客人都得排队上这一辆车,谁也没法叫到更多的车。面试

对应到实际的交互上是同样同样的:每当用户触发了一次 scroll 事件,咱们就为这个触发操做开启计时器。一段时间内,后续全部的 scroll 事件都会被看成“一辆车的乘客”——它们没法触发新的 scroll 回调。直到“一段时间”到了,第一次触发的 scroll 事件对应的回调才会执行,而“一段时间内”触发的后续的 scroll 回调都会被节流阀无视掉。npm

如今一块儿实现一个 throttle:浏览器

// fn是咱们须要包装的事件回调, interval是时间间隔的阈值
function throttle(fn, interval) {
  // last为上一次触发回调的时间
  let last = 0
  
  // 将throttle处理结果看成函数返回
  return function () {
      // 保留调用时的this上下文
      let context = this
      // 保留调用时传入的参数
      let args = arguments
      // 记录本次触发回调的时间
      let now = +new Date()
      
      // 判断上次触发的时间和本次触发的时间差是否小于时间间隔的阈值
      if (now - last >= interval) {
      // 若是时间间隔大于咱们设定的时间间隔阈值,则执行回调
          last = now;
          fn.apply(context, args);
      }
    }
}

// 用throttle来包装scroll的回调
const better_scroll = throttle(() => console.log('触发了滚动事件'), 1000)

Debounce: 最后一我的说了算

防抖的中心思想在于:我会等你到底。在某段时间内,无论你触发了多少次回调,我都只认最后一次。缓存

继续讲司机开车的故事。此次的司机比较有耐心。第一个乘客上车后,司机开始计时(好比说十分钟)。十分钟以内,若是又上来了一个乘客,司机会把计时器清零,从新开始等另外一个十分钟(延迟了等待)。直到有这么一位乘客,从他上车开始,后续十分钟都没有新乘客上车,司机会认为确实没有人须要搭这趟车了,才会把车开走。

咱们对比 throttle 来理解 debounce:在throttle的逻辑里,“第一我的说了算”,它只为第一个乘客计时,时间到了就执行回调。而 debounce 认为,“最后一我的说了算”,debounce 会为每个新乘客设定新的定时器。

如今一块儿实现一个 debounce:

// fn是咱们须要包装的事件回调, delay是每次推迟执行的等待时间
function debounce(fn, delay) {
  // 定时器
  let timer = null
  
  // 将debounce处理结果看成函数返回
  return function () {
    // 保留调用时的this上下文
    let context = this
    // 保留调用时传入的参数
    let args = arguments

    // 每次事件被触发时,都去清除以前的旧定时器
    if(timer) {
        clearTimeout(timer)
    }
    // 设立新定时器
    timer = setTimeout(function () {
      fn.apply(context, args)
    }, delay)
  }
}

// 用debounce来包装scroll的回调
const better_scroll = debounce(() => console.log('触发了滚动事件'), 1000)

用 Throttle 来优化 Debounce

debounce 的问题在于它“太有耐心了”。试想,若是用户的操做十分频繁——他每次都不等 debounce 设置的 delay 时间结束就进行下一次操做,因而每次 debounce 都为该用户从新生成定时器,回调函数被延迟了不可胜数次。频繁的延迟会致使用户迟迟得不到响应,用户一样会产生“这个页面卡死了”的观感。

为了不弄巧成拙,咱们须要借力 throttle 的思想,打造一个“有底线”的 debounce——等你能够,但我有个人原则:delay 时间内,我能够为你从新生成定时器;但只要delay的时间到了,我必需要给用户一个响应。这个 throttle 与 debounce “合体”思路,已经被不少成熟的前端库应用到了它们的增强版 throttle 函数的实现中:

// fn是咱们须要包装的事件回调, delay是时间间隔的阈值
function throttle(fn, delay) {
  // last为上一次触发回调的时间, timer是定时器
  let last = 0, timer = null
  // 将throttle处理结果看成函数返回
  
  return function () { 
    // 保留调用时的this上下文
    let context = this
    // 保留调用时传入的参数
    let args = arguments
    // 记录本次触发回调的时间
    let now = +new Date()
    
    // 判断上次触发的时间和本次触发的时间差是否小于时间间隔的阈值
    if (now - last < delay) {
    // 若是时间间隔小于咱们设定的时间间隔阈值,则为本次触发操做设立一个新的定时器
       clearTimeout(timer)
       timer = setTimeout(function () {
          last = now
          fn.apply(context, args)
        }, delay)
    } else {
        // 若是时间间隔超出了咱们设定的时间间隔阈值,那就不等了,不管如何要反馈给用户一次响应
        last = now
        fn.apply(context, args)
    }
  }
}

// 用新的throttle包装scroll的回调
const better_scroll = throttle(() => console.log('触发了滚动事件'), 1000)

document.addEventListener('scroll', better_scroll)

在 Vue 里使用 lodash 中的 Debouncing 和 Throttling

事件节流和防抖是提升性能或下降网络开销的好方法。虽然 Vue 1曾经支持对事件的节流和防抖,可是在Vue 2中为了保持核心的简单性,删除对事件的节流和防抖的支持。所以,在Vue 2对对事件进行防抖和节流咱们可使用 lodash 来作。

安装

能够经过 yarn 或 npm 安装 lodash。

# Yarn
$ yarn add lodash
# NPM
$ npm install lodash --save
注意:若是咱们不想导入 lodash的全部内容,而只导入所需的部分,则能够经过一些Webpack构建自定义来解决问题。 还可使用 lodash.throttlelodash.debounce等软件包分别安装和导入 lodash的各个部分。

throttling 方法

要对事件进行节流处理方法很是简单,只需将要调用的函数包装在lodash的_.throttle函数中便可。

<template>
  <button @click="throttledMethod()">Click me as fast as you can!</button>
</template>

<script>
import _ from 'lodash'

export default {
  methods: {
    throttledMethod: _.throttle(() => {
      console.log('I get fired every two seconds!')
    }, 2000)
  }
}
</script>

debouncing 方法

尽管节流在某些状况下颇有用,但通常状况咱们常用的是防抖。 防抖实质上将咱们的事件分组在一块儿,并防止它们被频繁触发。 要在Vue组件中使用节流,只需将要调用的函数包装在lodash的_.debounce函数中。

<template>
  <button @click="throttledMethod()">Click me as fast as you can!</button>
</template>

<script>
import _ from 'lodash'

export default {
  methods: {
    throttledMethod: _.debounce(() => {
      console.log('I only get fired once every two seconds, max!')
    }, 2000)
  }
}
</script>

代码部署后可能存在的BUG无法实时知道,过后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给你们推荐一个好用的BUG监控工具 Fundebug


参考:

Throttling and Debouncing in JavaScript
The Difference Between Throttling and Debouncing
Examples of Throttling and Debouncing
Remy Sharp’s blog post on Throttling function calls
前端性能优化原理与实践

交流

文章每周持续更新,能够微信搜索「 大迁世界 」第一时间阅读和催更(比博客早一到两篇哟),本文 GitHub https://github.com/qq449245884/xiaozhi 已经收录,整理了不少个人文档,欢迎Star和完善,你们面试能够参照考点复习,另外关注公众号,后台回复福利,便可看到福利,你懂的。

相关文章
相关标签/搜索