防抖是指触发高频事件后 n 秒内函数只会执行一次,如果 n 秒内高频事件再次被触发,则重新计算时间。
如果让一些事件响应函数的响应频率无限制,不仅会加重浏览器的负担,而且容易导致页面卡顿等影响用户的体验。所以使用防抖可以来限制事件处理函数的调用频率,提升用户的体验,同时又不影响实际的效果。
简单的防抖函数
利用 Javascript 闭包的特性,保存一个计时器 Timeout,在其子函数中对其进行更新。
1 | function debounce(fn, wait) { |
函数的运行过程如下:
- 触发函数后创建计时器
- 在计时器结束之前若再次触发函数则更新重置计时器
- 计时器结束时触发函数 fn
React 函数式组件中的防抖函数
上面的 debounce 函数在 React 中无法生效,原因是函数式组件每次渲染,函数都会被重建,导致 debounce 函数中的 timer 会重新创建,进而导致防抖失效。目前网络上提供了两种比较常用的方法:
使用 useRef 来缓存计时器
1
2
3
4
5
6
7
8
9
10
11
12
13function useDebounce(fn,delay){
//fn为需要防抖的函数
const refTimer = useRef();
return function f(...args){
if(refTimer.current){
clearTimeout(refTimer.current);
}
refTimmer.current = setTimeout(() => {
fn(args);
},delay)
}
}使用 useCallback 缓存函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16function Debounce(){
const click = useCallback(clickInner(),[]);
function clickInner(){
let timer;
return function (e){
if(timer){
clearTimeout(timer);
}
timer = setTimeout(() => {
//todo
//事件 e 的相关操作
},1000)
}
}
}
以上两种方法时防抖函数在 React 组件式函数中的应用。
React + Hook + Typescript 中的防抖函数
Typescript 中增加了静态类型定义,对上面的函数稍加修改即可:
1 | import { useCallback, useEffect, useRef } from "react"; |
调用方式如下(以 Ant Design 中的 Slider 组件为例):
1 | const onChangeR = useDebounce((newValue: number) => { |
实际上自己写的防抖函数在 Ant Design 的 Slider 组件上的表现并不好,其 Slider 组件本身就提供了 onChange 和 onAfterChange 两个不同的 API,其中 onAfterChange 与 onMouseUp 触发时机一致,传入值为 Slider 的当前值,完全可以将 onAfterChange 当作是防抖后的 onChange 使用。
在编写和使用自定义的 useDebounce 的时候,还遇到了一些其他的困难,它们与 React Hook 的特性相关,我将在之后的 React 学习笔记中进行介绍和分析。