本文已参与「 新人创作礼 」活动,一起开启掘金创作之路
防抖节流这样在前端早期的浏览器很多一些场景是经常出现。
例如:会到顶部的这个简单应用。
1.在早期这个icon要跟随浏览器的滚动条往下滚动,特别是在早版本的( ie6 )。这个时候你就知道我是多久的前端开发了吧。position:fiexd;
ie6 的时候貌似还不知道。
2.window.resize 当窗体发生变化的时候通过resize去监听。当然现在有牛逼的media
查询方案。
3.搜索框收入字符的想服务器发送请求。( 尽量减少前端 + 服务端 )请求优化。
等等这些都会用到防抖节流。
我们要搞清楚什么时候用防抖,什么时候用节流。
防抖:如果是搜索框,当我输入abcd的时候,你是希望我最后一次节点输入才触发查询还是输入a就开始查询呢
明显是最后d的时候查询会更优。也就是归纳说,在时间被触发之后,n毫秒/秒之后执行调用,在规定的时间内多次触发,后面的会把前面的事件覆盖掉,然后重新开始计时。
符合防抖的应用有搜索、改变窗体大小,滚动条跟随。等等都是可以用防抖函数。
节流:当我们点击一个按钮的时候,需要得到服务端的请求,例如我在1秒内连续点击了5次,最优化方案是,我只需要第一次,在第一次结果没有返回的情况下,把剩余的4次丢弃掉。或者说,不管是否返回,3秒内不能在发送新的请求。这个都算是节流。规定在一个单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效
。
在现在前端开发,这些事情早已经被浏览器的新特性,或者Utils包给我们解决了。流入节流场景 React-Saga就是一个非常好用的一个中间件。还有React-query都提供这些业务场景的考虑。
而且loadash
函数库也提供了很多。
import {debounce , throttle} from 'lodash'
debounce(function() {
this.callback() //
},2000)
debounce(function() {
this.callback() //
},2000)
是不是超级的方便。但是我们该如何自己去写一个呢?
/**
* 防抖, 触发后N秒,只执行一次
* /
*
*/
function debounce(fn , time) {
let args = [...arguments];
//console.log ( [].slice.call(arguments) );
let context = this;
let timeout = null;
return function (arguments){
if (timeout) {
clearTimeout(timeout);
timeout = null;
console.log (timeout);
}
if (!timeout) {
timeout = setTimeout(()=>{
args = args.concat(...arguments)
fn.apply(context,args);
},time);
}
}
}
· 防抖用的setTimeout方法来做的事情,比较的简单。没有太多好说的,不懂可以留言。
function exec() {
console.log ("debounce!!!" ,new Date(), ...arguments);
}
let o = debounce( exec , 2000 , 1,2,3,4,5);
o("a"); //不输出
o("b"); //输出 b
这样的一个封装是不是立马让你要执行的function都编程防抖函数。是不是很方便。当然不行这么复杂,就不需要放回一个function,直接一个立即执行函数即可。( 可以根据你的业务自己配置而已 )
/**
* 节流、用时间函数更为准确
*/
function throttle (fn , time) {
let args = arguments;
let context = this;
let firstTime = 0;
return function (flag) {
let curTime = Date.now();
if (firstTime + time < curTime ) {
//console.log (flag);
args = [].slice.call(args);
if (flag) {
args.push(flag);
}
firstTime = Date.now();
fn.apply(context , args);
}
}
}
function exec() {
console.log ("throttle" , ...arguments);
}
let o = throttle(exec , 2000 , 2,3,6);
o("a");o("b"); //结果中有a,无b
这个我使用的 时间戳就算的方式,当然同样可以用settimeout的方法。settimeout会不太准确。可以用setInterval但是要记住清掉他的执行。 ( 否则就会死循环 )
PS:前端开发小技巧在工作中,数不胜数,只要你愿意去尝试,去玩,其实还是非常的有意思。这2个例子中,你们发现没有有几个问题,例如arguments到底是什么类型, 有多少方法可以处理它 ?