当在前端开发中处理事件时,防抖和节流是两个常用的技术,用于限制事件的触发频率,提高性能和用户体验。
防抖 (Debounce): 当事件被触发时,防抖技术会在一定的时间内(比如300毫秒)等待更多的事件,只在最后一次事件触发后才执行相应的操作。如果在这段时间内又触发了该事件,那么重新计时。这样的效果可以避免频繁触发事件导致的性能问题。
应用场景举例:
- 用户输入搜索框:比如百度搜索,输入内容时需要根据内容加载候选列表,但又不能每输入一个字符就要获取数据,这样过度频繁获取数据会造成一些性能问题及资源浪费等等,因此要在输入过程中有稍微停顿时再获取数据;
- 滚动条事件监听:有时候需要监听滚动条拉至指定位置时去做一些事,比如下拉至底部时要加载下一批数据,如果不做合适的控制,当滚轮不断上下滚动,会多次触发加载数据的行为,同样会造成一些性能问题及资源浪费等等,因此需要防抖来解决这些问题。
节流 (Throttle): 节流技术与防抖技术类似,但它是在固定的时间间隔(比如300毫秒)内执行事件的操作,而不是等待最后一次事件触发。如果在这段时间内触发了多次事件,只有在时间间隔过去后才会执行一次操作。这样的效果可以减少事件触发的频率,特别适用于需要控制频繁操作的情况。
应用场景举例:
- 防止按钮重复点击:当用户点击按钮时,可能会因为网络延迟或用户误操作导致多次触发相同的操作。为了避免重复执行,可以使用节流技术,在一定时间内只允许执行一次按钮点击操作。
- 窗口调整事件:当窗口大小调整时,浏览器会触发resize事件。如果在调整窗口大小的过程中频繁执行某些操作,可能会导致浏览器性能下降。使用节流函数可以降低操作的执行频率。
以上举例只是针对一些实际的应用场景,像窗口调整事件也有用 防抖 (Debounce) 的场景,主要取决于你的实际需求,比如窗口大小变化后,只想执行一次操作,可以用防抖,如果想在窗口变化过程中做一些事(比如做些什么计算之类的),可以用节流。
防抖示例代码
/** 防抖
* @param {function} func 需要防抖的执行函数
* @param {number} delay 函数执行延时时长(ms)
*/
function debounce(func, delay) {
var timer;
return function (...args) {
clearTimeout(timer);
timer = setTimeout(function () {
func.apply(this, args);
}, delay);
};
};
实测效果:
// 使用
const handleInput = debounce(() => {
console.log("防抖演示:一秒内不断触发,稍作停顿后只执行最后一次触发的事件");
}, 1000);
<input placeholder="debounce" onInput={handleInput} />
节流示例代码
/** 节流
* @param {function} func 需要节流的执行函数
* @param {number} delay 函数执行频率(时间间隔:ms)
*/
function throttle(func, delay) {
var lastExecutionTime = 0;
return function (...args) {
var currentTime = Date.now();
if (currentTime - lastExecutionTime >= delay) {
func.apply(this, args);
lastExecutionTime = currentTime;
}
};
};
或者使用 setTimeout:
/** 节流
* @param {function} func 需要节流的执行函数
* @param {number} delay 函数执行频率(时间间隔:ms)
*/
function throttle(func, delay) {
var timer;
return function (...args) {
if (!timer) {
timer = setTimeout(function () {
func.apply(this, args);
timer = null;
}, delay);
}
};
}
实测效果:
// 使用
const handleClick = throttle(() => {
console.log("节流演示:不管点击多快,只会每隔一秒执行一次");
}, 1000);
<button onClick={handleClick}>throttle</button>
React Hook 版
防抖Hook:
// useDebounce.js
import { useRef, useEffect, useCallback } from 'react';
/** 防抖
* @param {function} func 需要防抖的执行函数
* @param {number} delay 函数执行延时时长(ms)
* @param {Array<any>} deps 自定义hook依赖
*/
export function useDebounce(func, delay, deps = []) {
const { current } = useRef({ func: () => { }, timer: null });
useEffect(function () {
current.func = func;
}, [func]);
return useCallback(function (...args) {
if (current.timer) {
clearTimeout(current.timer);
}
current.timer = setTimeout(function () {
current.func.call(this, ...args);
}, delay);
}, deps);
}
使用示例:
import { useDebounce } from './useDebounce';
function App() {
const handleInput = useDebounce(() => {
console.log("防抖演示:一秒内不断触发,稍作停顿后只执行最后一次触发的事件");
}, 1000);
return (
<div>
<input placeholder="debounce" onInput={handleInput} />
</div>
);
}
export default App;
节流Hook:
// useThrottle.js
import { useRef, useEffect, useCallback } from 'react';
/** 节流
* @param {function} func 需要节流的执行函数
* @param {number} delay 函数执行频率(时间间隔:ms)
* @param {Array<any>} deps 自定义hook依赖
*/
export function useThrottle(func, delay, deps = []) {
const { current } = useRef({ func: () => { }, timer: null });
useEffect(function () {
current.func = func;
}, [func]);
return useCallback(function (...args) {
if (!current.timer) {
current.timer = setTimeout(function () {
current.timer = null;
}, delay);
current.func.call(this, ...args);
}
}, deps);
}
使用示例:
import { useThrottle } from './useThrottle';
function App() {
const handleClick = useThrottle(() => {
console.log("节流演示:不管点击多快,只会每隔一秒执行一次");
}, 1000);
return (
<div>
<button onClick={handleClick}>throttle</button>
</div>
);
}
export default App;
以上代码只进行了简单的封装,提供大概的思路,可根据各自需要进行扩展(比如可暂停,取消防抖/节流等功能)