函数防抖和节流
我们经常会需要绑定一些持续触发的事件,如 resize、scroll、mousemove 等等,但有些时候我们并不希望在事件持续触发的过程中那么频繁地去执行函数。函数防抖和节流就是一种很好的解决办法。
一、概念?
- 所谓防抖,就是事件频繁触发的时候,部分代码在指定时间间隔内都不会触发,只在结束的时候经过指定时间后触发一次。(在特定的时间内,没有触发特定条件,就执行一次任务)
- 所谓节流,就是事件频繁触发的时候,部分代码第一次会立即触发,后续如果持续触发事件函数(不论触发的频率如何),都会每间隔指定时间触发一次。(在特定的时间内,无论触发多次条件,仅执行一次任务)
二、区别?
-
-
函数节流在特定时间内会固定触发一次任务,并且是规律的
- 相比之下,函数通过节流的方式调用的次数通常会比通过防抖方式调用的次数多
三、使用场景?
-
-
表单验证,验证邮箱的格式,停止输入时再做验证
-
onresize onscroll onmousemove
-
对于高频事件一般要做触发频率的显示
-
具体案例
下面的Vue例子中,想要实现左侧图片中的搜索关键字功能,这时给input标签注册了一个input事件,然而,input事件在输入框每次值改变时都会触发,如右图所示。
这时,我们监测网络请求时就会发现一个问题,如下图。这样频繁地请求会给服务器造成很大的压力。
上面input事件绑定的函数代码为:
async handleInput () { let resultRes = await request('goods/qsearch', 'GET', {query: this.keyword}) this.resultList = resultRes.data.message }
想要解决这个频繁请求的问题,可以进行如下的代码操作(可以限制请求次数、但不是防抖):
async handleInput () { if (this.isLoading) { return } this.isLoading = true let resultRes = await request('goods/qsearch', 'GET', {query: this.keyword}) this.resultList = resultRes.data.message this.isLoading = false }
可以看出,上面的代码中是通过在data中定义一个变量isLoading并设置初始值为false,在初次请求时,将isLoading的值更改为true,只有当这次请求成功时才会重新将isLoading的值改为false。如果当次请求不成功,isLoading的值为true的情况下,调用这个函数时,会直接返回而不发送请求。
这时在重新监测一下网络请求,结果如下。可以看出请求的次数有所减少。
但是,在网络状况好的情况下,请求还会过于频繁,下面就使用防抖的方法。
handleInput () { // 函数防抖,限制请求次数 clearTimeout(this.timer) this.timer = setTimeout(async () => { let resultRes = await request('goods/qsearch', 'GET', {query: this.keyword}) this.resultList = resultRes.data.message }, 1000) }
上面的代码中,通过设置延时定时器,先将函数放到定时器队列中,1秒后才执行定时器内部操作。但是,因为有了clearTimeout这个清除定时器的操作,当输入快速而频繁触发函数handleInput时,每次都会清除上一次触发时创建的定时器。只有当用户输入并间隔一秒后,才会执行定时器中的代码,这就有效地减少了网络请求次数。效果如下图。
可以看出,这样有效地减轻了服务器压力,当然,通过节流的方式也可以解决这个问题。
async handleInput () { if (this.isLoading) { return } this.isLoading = true let resultRes = await request('goods/qsearch', 'GET', {query: this.keyword}) this.resultList = resultRes.data.message this.timer = setTimeout(() => { this.isLoading = false }, 1000) }
但是如果考虑实际需求的话,使用防抖的方式解决上述情况的效果要比节流好(因为用户就是需要在每次输入结束的时候触发请求代码,调用接口获取数据)。
我们可以举一个适合使用函数节流的例子,就是页面触底时需要加载更多数据的时候。(后面有实际例子的时候再补上)