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

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

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

Throttle: 第一个人说了算

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

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

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

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

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

现在一起实现一个 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.throttle和lodash.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。

以上这篇Vue 中使用lodash对事件进行防抖和节流操作就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • 在vue-cli中引入lodash.js并使用详解

    lodash 是一个一致性.模块化.高性能的 JavaScript 实用工具库. 在vue官方文档中使用了lodash中的debounce函数对操作频率做限制.其引入的方式是直接引入了js <script src="https://cdn.jsdelivr.net/npm/lodash@4.13.1/lodash.min.js"></script> 而现在我们使用vue-cli脚手架搭建的项目在这样使用,明显会很不合适.所以我们需要通过npm来安装 $ npm

  • Vue最新防抖方案(必看篇)

    函数防抖(debounce):当持续触发事件时,一定时间段内没有再触发事件,事件处理函数才会执行一次,如果设定的时间到来之前,又一次触发了事件,就重新开始延时.举个栗子,持续触发scroll事件时,并不执行handle函数,当1000毫秒内没有触发scroll事件时,才会延时触发scroll事件. 函数节流(throttle):当持续触发事件时,保证一定时间段内只调用一次事件处理函数.节流通俗解释就比如我们水龙头放水,阀门一打开,水哗哗的往下流,秉着勤俭节约的优良传统美德,我们要把水龙头关小点,

  • 深入了解JavaScript 防抖和节流

    概述 说明 在项目过程中,经常会遇到一个按钮被多次点击并且多次调用对应处理函数的问题,而往往我们只需去调用一次处理函数即可.有时也会遇到需要在某一规则内有规律的去触发对应的处理函数,所以就需要使用到函数防抖与函数节流来帮助我们实现我们想要的结果以及避免不必要的问题产生. 函数防抖(debounce) 定义:当持续触发事件时(如连续点击按钮多此),一定时间段内没有再触发事件,事件处理函数才会执行一次,如果设定的时间到来之前,有一次触发了事件,就重新开始延时. 原理:维护一个计时器,规定在延时时间后

  • Vue 中使用lodash对事件进行防抖和节流操作

    有些浏览器事件可以在短时间内快速触发多次,比如调整窗口大小或向下滚动页面.例如,监听页面窗口滚动事件,并且用户持续快速地向下滚动页面,那么滚动事件可能在 3 秒内触发数千次,这可能会导致一些严重的性能问题. 如果在面试中讨论构建应用程序,出现滚动.窗口大小调整或按下键等事件请务必提及 防抖(Debouncing) 和 函数节流(Throttling)来提升页面速度和性能.这两兄弟的本质都是以闭包的形式存在.通过对事件对应的回调函数进行包裹.以自由变量的形式缓存时间信息,最后用 setTimeou

  • 在VUE中使用lodash的debounce和throttle操作

    说明: debounce和throttle在脚手架的使用,此处以防抖函数debounce为例避免按钮被重复点击 引入: import lodash from 'lodash' 使用: 直接使用debounce方法 // 审核 audit: lodash.debounce(function() { this.$refs['model'].saveTotalResult(1).then(() => { const reportId = this.activeReport.id; report.aud

  • Vue中使用Lodash的实现示例

    安装 cnpm i -S lodash 全局引入 import _ from 'lodash' Vue.prototype._ = _ 使用 在任何地方使用_或者this._即可调用lodash 对象数组排序 let users = [ { user: 'a', age: 48 }, { user: 'b', age: 34 }, { user: 'a', age: 42 }, { user: 'b', age: 55 } ]; let c = this._.orderBy(users, ['a

  • 在vue中使用Echarts利用watch做动态数据渲染操作

    依旧直接上代码- 首先安装引入Echarts,我是直接把Echarts挂到VUE全局变量上了 //引入echarts import Vue from 'vue'; import echarts from 'echarts'; Vue.prototype.$echarts = echarts; <template> <div class="demo-container"> <div ref="chart_wrap" class="

  • vue中element 的upload组件发送请求给后端操作

    1.用到了before-upload属性, 用于在上传文件前的校验,并且发送请求给后端,传输格式进行文件流传输 什么都不用设置,action属性随便设置,不能为空即可! 在before-upload属性的方法中的代码如下: var _this = this; debugger; // var files=file.target.files[0]; debugger; const isJPG = file.type === "image/jpeg"; const isLt2M = fil

  • vue 中的动态传参和query传参操作

    Vue router 如何传参 params.query 是什么? params:/router1/:id,这里的 id 叫做 params.例如/router1/123, /router1/789 query:/router1?id=123,这里的 id 叫做 query.例如/router1?id=456 query 方式传参和接收参数 传参: this.$router.push({ path:'/xxx' query:{ id:id } }) this.$router.push 传参时,

  • 在vue中通过render函数给子组件设置ref操作

    正常我们的写法是,这样ref不会生效,h是作用在渲染的时候的,而ref是渲染之后才创建的,因此在h函数中使用ref是无效的. render: (h, params) => { return h(expandRow, { ref:'child', props: { row: params.row } }) } 我们常见h函数的用法是: render: (h) => { return h(ele) } => 是es6的用法,相当于 (h) => {} 相当于 function(){},

  • VUE中如何实现阻止事件冒泡

    目录 如何阻止事件冒泡 科普 阻止click事件冒泡(防止触发另一个事件)的方法 方法一 方法二 如何阻止事件冒泡 当父子元素中都有点击事件的时候,为了让触发子元素中的事件时,不去触发父元素中的事件,可以在子元素事件中添加stop来阻止事件冒泡. 如图,在button的点击事件中加上stop,触发addStatus的时候,就不会去触发父元素中的showColorStatus事件. 科普 .stop 是阻止冒泡行为,不让当前元素的事件继续往外触发,如阻止点击div内部事件,触发div事件 .pre

  • 基于Vue中使用节流Lodash throttle详解

    在Vue中使用 Lodash.throttle 来做节流 在Vue中,有时需要对ajax请求提交进行节流操作.这时候,如果页面在请求成功之后会跳转,使用vue指令once或者加载一个loading遮罩禁止点击即可,但如果请求之后不跳转,并且loading不适用时,需要通过其他方式来实现节流,比如通过标识位判断等,这里我们着重说一下如何通过lodash.throttle来实现节流 lodash lodash是一个广受欢迎的js工具库,其中包含了各种各样的工具函数,方便开发时不需要反复造轮子,更关注

  • 对vue中v-on绑定自定事件的实例讲解

    关于官网vue中v-on绑定自定义事件的个人理解 对官网实例进行了一些修改,如下图: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>v-on绑定自定义事件</title> <script src="vue.js"></script> </head>

随机推荐