有些浏览器事件可以在短时间内快速触发多次,比如调整窗口大小或向下滚动页面。例如,监听页面窗口滚动事件,并且用户持续快速地向下滚动页面,那么滚动事件可能在 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函数中即可。

Click me as fast as you can!

import _ from 'lodash'

export default {

methods: {

throttledMethod: _.throttle(() => {

console.log('I get fired every two seconds!')

}, 2000)

}

}

debouncing 方法

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

Click me as fast as you can!

import _ from 'lodash'

export default {

methods: {

throttledMethod: _.debounce(() => {

console.log('I only get fired once every two seconds, max!')

}, 2000)

}

}

参考:

更多编程相关知识,请访问:编程入门!!

php如何做防抖,Vue中怎么对事件进行防抖和节流操作?相关推荐

  1. vue中 点击事件的写法_vue中的事件:原生事件与自定义事件__Vue.js

    模板编译 processAttrs 对于ast attributes处理(v-on/@) 利用onRE与dirRE来捕获事件 这里最重要的就是dynamic的判断,vue中可以用动态参数来命名事件名称 ...

  2. vue-html5-editor接收数据,在vue中获取wangeditor的html和text的操作

    目的:vue 中获取 wangeditor 的 html 和 text. 补充知识:vue-cli webpack 引入 wangeditor(轻量级富文本框) 1:使用npm下载: //(注意 wa ...

  3. vue鼠标移动上去提示_关于如何处理vue中鼠标悬停事件的详细说明

    最后,在查看结果中,发现滑过鼠标事件将触发该事件,但它将闪烁并清除. 当鼠标停留在单元格上时鼠标滑过事件,遮罩层将消失并重复出现. 开关. 为了缓解这种情况,setTimeout也用于延迟显示和隐藏在 ...

  4. vue中enter回车键事件

    项目中在搜索商品时,在没有搜索按钮的情况下,刚开始是写的当用户输入完成后,input框失去焦点blur事件处理,产品提议用户输入后,按enter回车键返回搜索结果. vue中失去焦点事件写法:@blu ...

  5. Vue中监听键盘事件

    Vue中监听键盘事件 在一些搜索框中,我们往往需要监听键盘的按下(onkeydown)或抬起(onkeyup)事件以进行一些操作.在原生js或者jQuery中,我们需要判断e.keyCode的值来获取 ...

  6. vue中使用mousewheel事件在火狐浏览器中不生效

    vue中使用mousewheel事件在火狐浏览器中不执行 将mousewheel修改为wheel就可以了.

  7. 01-vue(v-text,插值语法,v-html,v-model,v-on,v-bind,vue中的this,事件按键修饰符)

    01- vue是什么: <!DOCTYPE html> <html lang="en"><head><meta charset=" ...

  8. vue中input绑定事件

    做的越多遇到的问题越多,解决的问题越多,你会的就越多,加油 我们都知道vue中是可以双向数据绑定的,但是有时候我们的事件会和双向绑定冲突. input中如果用了v-model,那么给它绑定的事件就不会 ...

  9. 「后端小伙伴来学前端了」关于Vue中的自定义事件,组件绑定自定义事件实现通信

    傍晚的月亮 前言 原本这篇打算写Vue中的那个全局事件总线的原理,但是发现自己少写了这个自定义事件,不讲明白这个自定义事件的操作,不好写全局事件原理,于是就有了这篇文章拉. 一.v-on指令 要讲自定 ...

  10. 「后端小伙伴来学前端了」Vue中利用全局事件总线实现组件之间通信

    月亮啊月亮 你能照见南边,也能照见北边 照见她,你跟她说一声,就说我想她了. 前言 前一篇文章写了 vue 中利用 Props 实现组件之间的通信,那种方式是最简单也是最基础的组件之间的通信方式.父组 ...

最新文章

  1. vue 时间安排表
  2. 4行代码搞定iframe高度自动变化,完美兼容(转)
  3. 虚拟机下Linux安装图解之三:Linux发行版本之Red Hat 9 安装
  4. linux目录档案权限详解,五、Linux的档案权限与目录配置
  5. JBOSS优化--比较有用的生产环境配置
  6. 《Total Commander:万能文件管理器》——第8.3节.张学思版预装的插件
  7. mysql和mongodb替换字段中某字符
  8. 第七章 控制PL/SQL错误
  9. 单车家族 结对项目二
  10. javascript Array学习与使用
  11. 文档管理 linux,Linux 文档管理
  12. P1041 传染病控制
  13. mybatis-generator-gui--一个mybatis代码自动生成界面工具
  14. oracle查询显示小写,oracle查询区分大小写
  15. python实战演练一:抓取我自己csdm博客信息的标题和文章链接,并存入文件夹《抓取信息》
  16. 【Missing separate debuginfos, use: debuginfo-install glibc-2.17-222.el7.x86_64】 解决方法
  17. 1038: 绝对值最大 Python
  18. 理解充分条件与必要条件
  19. Python实现英文词频统计:以hamlet为例
  20. 计算机不能上网的故障和解决办法,电脑无法上网的原因分析及解决办法

热门文章

  1. C2C、O2O、B2B、B2C 是什么?区别在哪里?
  2. python让solidworks自动建模_让机器学习自动帮我们建模,这4个Python库能让你大开眼界...
  3. 春来夏往,秋收冬藏,我们来日方长(第一章)
  4. IPv6技术精要--第12-13章 ICMPv6和邻居发现协议(ND协议)
  5. ps后期调色教程,ps怎么后期调色步骤图
  6. java 根据条件从List中筛选出符合条件的集合
  7. Qt 的 linuxFB XCB KMS XCB Wayland
  8. html id命名规范,CSS常用类/ID命名规范
  9. 【ICPC-303】hau 1874 畅通工程续
  10. 如果你没有钱,没有能力,没有人脉,你就这样干!