前端防抖节流在前端场景的应用

352 阅读3分钟

本文已参与「 新人创作礼 」活动,一起开启掘金创作之路

关于如何进行图片优化 - 适合的才是最好的

防抖节流这样在前端早期的浏览器很多一些场景是经常出现。

例如:会到顶部的这个简单应用。

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到底是什么类型, 有多少方法可以处理它 ?