Debounce
debounce 原意
消除抖动
,对于事件触发频繁的场景,只有最后由程序控制的事件是有效的。
防抖函数,我们需要做的是在一件事触发的时候设置一个定时器使事件延迟发生,在定时器期间事件再次触发的话则清除重置定时器,直到定时器到时仍不被清除,事件才真正发生。
const debounce = (fun, delay) => {
let timer;
return (...params) => {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(() => {
fun(...params);
}, delay);
};
};
如果事件发生使一个变量频繁变化,那么使用debounce
可以降低修改次数。通过传入修改函数,获得一个新的修改函数来使用。
如果是class
组件,新函数可以挂载到组件this
上,但是函数式组件局部变量每次render
都会创建,debounce
失去作用,这时需要通过useRef
来保存成员函数(下文throttle
通过useRef
保存函数),是不够便捷的,就有了将debounce
做成一个hook
的必要。
function useDebounceHook(value, delay) {
const [debounceValue, setDebounceValue] = useState(value);
useEffect(() => {
let timer = setTimeout(() => setDebounceValue(value), delay);
return () => clearTimeout(timer);
}, [value, delay]);
return debounceValue;
}
在函数式组件中,可以将目标变量通过useDebounceHook
转化一次,只有在满足delay
的延迟之后,才会触发,在delay
期间的触发都会重置计时。
配合useEffect
,在debounce value
改变之后才会做出一些动作。下面的text
这个state
频繁变化,但是依赖的是debounceText
,所以引发的useEffect
回调函数却是在指定延迟之后才会触发。
const [text,setText]=useState('');
const debounceText = useDebounceHook(text, 2000);
useEffect(() => {
// ...
console.info("change", debounceText);
}, [debounceText]);
function onChange(evt){
setText(evt.target.value)
}
上面一个搜索框,输入完成1
秒(指定延迟)后才触发搜索请求,已经达到了防抖的目的。
Throttle
throttle 原意
节流阀
,对于事件频繁触发的场景,采用的另一种降频策略,一个时间段内只能触发一次。
节流函数相对于防抖函数用在事件触发更为频繁的场景上,滑动事件,滚动事件,动画上。
看一下一个常规的节流函数 (ES6):
function throttleES6(fn, duration) {
let flag = true;
let funtimer;
return function () {
if (flag) {
flag = false;
setTimeout(() => {
flag = true;
}, duration