优雅实现防抖与节流
🤔 为什么需要防抖与节流?
在前端开发中,我们经常会遇到一些高频触发的事件,比如:
- 窗口缩放(resize)
- 页面滚动(scroll)
- 输入框输入(input)
- 按钮频繁点击(click)
这些事件如果不加以控制,会导致大量的函数调用,从而影响页面性能。想象一下:用户在输入框快速输入时,每次按键都触发一次API请求------这不仅浪费带宽,还会导致页面卡顿!
防抖(Debounce)和节流(Throttle)就是为了解决这个问题而生的。它们可以有效地控制函数的执行频率,提升页面性能和用户体验。
💡 基础概念与实现
什么是防抖(Debounce)?
防抖的核心思想是:如果一个函数被频繁调用,只有当调用停止一段时间后,才执行最后一次调用。
形象地说:就像坐电梯,只有当没有人再进入电梯时,电梯才会关门运行。
防抖的基础实现
javascript
/**
* 防抖函数
* @param {Function} func - 要执行的函数
* @param {number} delay - 延迟时间(毫秒)
* @returns {Function} 防抖处理后的函数
*/
function debounce(func, delay) {
let timeoutId;
return function(...args) {
// 清除之前的定时器
clearTimeout(timeoutId);
// 设置新的定时器
timeoutId = setTimeout(() => {
// 执行函数,保持this上下文
func.apply(this, args);
}, delay);
};
}
// 使用示例
const debouncedSearch = debounce((keyword) => {
console.log('搜索:', keyword);
// 这里可以发送API请求
}, 300);
// 在输入框事件中使用
document.getElementById('searchInput').addEventListener('input', (e) => {
debouncedSearch(e.target.value);
});
什么是节流(Throttle)?
节流的核心思想是:限制函数在一定时间内只能执行一次。
形象地说:就像水龙头,即使一直开着,也只会每隔一段时间流出一滴水。
节流的基础实现
javascript
/**
* 节流函数
* @param {Function} func - 要执行的函数
* @param {number} limit - 时间限制(毫秒)
* @returns {Function} 节流处理后的函数
*/
function throttle(func, limit) {
let inThrottle;
return function(...args) {
if (!inThrottle) {
// 执行函数,保持this上下文
func.apply(this, args);
// 标记为已执行
inThrottle = true;
// 一段时间后重置标记
setTimeout(() => {
inThrottle = false;
}, limit);
}
};
}
// 使用示例
const throttledScroll = throttle(() => {
console.log('滚动位置:', window.scrollY);
// 这里可以处理滚动事件
}, 200);
// 在滚动事件中使用
window.addEventListener('scroll', throttledScroll);
🚀 React中的防抖与节流实现
在React中,我们可以将防抖和节流封装成自定义Hook,以便在组件中更方便地使用。
自定义防抖Hook:useDebounce
javascript
import { useEffect, useState } from 'react';
/**
* 防抖Hook
* @param {any} value - 要防抖的值
* @param {number} delay - 延迟时间(毫秒)
* @returns {any} 防抖后的值
*/
function useDebounce(value, delay) {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
// 设置定时器
const handler = setTimeout(() => {
setDebouncedValue(value);
}, delay);
// 清理函数:组件卸载或依赖变化时清除定时器
return () => {
clearTimeout(handler);
};
}, [value, delay]);
return debouncedValue;
}
// 使用示例
function SearchComponent() {
const [keyword, setKeyword] = useState('');
const debouncedKeyword = useDebounce(keyword, 300);
// 当防抖后的关键词变化时,发送API请求
useEffect(() => {
if (debouncedKeyword) {
console.log('搜索:', debouncedKeyword);
// 发送API请求
}
}, [debouncedKeyword]);
return (
<input
type="text"
value={keyword}
onChange={(e) => setKeyword(e.target.value)}
placeholder="输入关键词搜索..."
/>
);
}
自定义节流Hook:useThrottle
javascript
import { useRef, useState, useEffect } from 'react';
/**
* 节流Hook
* @param {any} value - 要节流的值
* @param {number} limit - 时间限制(毫秒)
* @returns {any} 节流后的值
*/
function useThrottle(value, limit) {
const [throttledValue, setThrottledValue] = useState(value);
const lastRan = useRef(Date.now());
useEffect(() => {
const handler = setTimeout(() => {
if (Date.now() - lastRan.current >= limit) {
setThrottledValue(value);
lastRan.current = Date.now();
}
}, limit - (Date.now() - lastRan.current));
return () => {
clearTimeout(handler);
};
}, [value, limit]);
return throttledValue;
}
// 使用示例
function ScrollComponent() {
const [scrollY, setScrollY] = useState(0);
const throttledScrollY = useThrottle(scrollY, 200);
// 监听滚动事件
useEffect(() => {
const handleScroll = () => {
setScrollY(window.scrollY);
};
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, []);
// 当节流后的滚动位置变化时,执行处理
useEffect(() => {
console.log('滚动位置:', throttledScrollY);
// 处理滚动事件
}, [throttledScrollY]);
return <div>当前滚动位置:{throttledScrollY}</div>;
}
🎯 Vue 3中的防抖与节流实现
在Vue 3中,我们可以使用Composition API来实现防抖和节流功能。
自定义防抖Composable:useDebounce
vue
<script setup>
import { ref, watch } from 'vue';
/**
* 防抖Composable
* @param {any} initialValue - 初始值
* @param {number} delay - 延迟时间(毫秒)
* @returns {Object} 包含原始值和防抖后的值
*/
function useDebounce(initialValue, delay) {
const value = ref(initialValue);
const debouncedValue = ref(initialValue);
let timeoutId;
// 监听值的变化,设置防抖
watch(value, (newValue) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
debouncedValue.value = newValue;
}, delay);
});
return {
value,
debouncedValue
};
}
// 使用示例
const { value: keyword, debouncedValue: debouncedKeyword } = useDebounce('', 300);
// 监听防抖后的值变化
watch(debouncedKeyword, (newKeyword) => {
if (newKeyword) {
console.log('搜索:', newKeyword);
// 发送API请求
}
});
</script>
<template>
<input
v-model="keyword"
type="text"
placeholder="输入关键词搜索..."
/>
</template>
自定义节流Composable:useThrottle
vue
<script setup>
import { ref, watch } from 'vue';
/**
* 节流Composable
* @param {any} initialValue - 初始值
* @param {number} limit - 时间限制(毫秒)
* @returns {Object} 包含原始值和节流后的值
*/
function useThrottle(initialValue, limit) {
const value = ref(initialValue);
const throttledValue = ref(initialValue);
let lastRan = Date.now();
let timeoutId;
// 监听值的变化,设置节流
watch(value, (newValue) => {
const now = Date.now();
if (now - lastRan >= limit) {
throttledValue.value = newValue;
lastRan = now;
} else {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
throttledValue.value = newValue;
lastRan = Date.now();
}, limit - (now - lastRan));
}
});
return {
value,
throttledValue
};
}
// 使用示例
import { onMounted, onUnmounted } from 'vue';
const { value: scrollY, throttledValue: throttledScrollY } = useThrottle(0, 200);
// 监听滚动事件
onMounted(() => {
const handleScroll = () => {
scrollY.value = window.scrollY;
};
window.addEventListener('scroll', handleScroll);
onUnmounted(() => {
window.removeEventListener('scroll', handleScroll);
});
});
// 监听节流后的滚动位置变化
watch(throttledScrollY, (newScrollY) => {
console.log('滚动位置:', newScrollY);
// 处理滚动事件
});
</script>
<template>
<div>当前滚动位置:{{ throttledScrollY }}</div>
</template>
⚠️ 注意事项与最佳实践
1. 防抖与节流的选择
- 防抖适合:输入框搜索、表单验证、窗口大小调整等需要等待用户操作完成后再执行的场景
- 节流适合:滚动事件、鼠标移动、按钮点击频率限制等需要限制执行频率的场景
2. 延迟时间的选择
- 输入框搜索:一般300-500ms
- 滚动事件:一般200-300ms
- 窗口调整:一般500-1000ms
- 按钮点击:一般500-1000ms
3. 内存泄漏问题
- 确保在组件卸载或不再需要时,清除定时器
- 使用React的useEffect清理函数或Vue的onUnmounted钩子
4. 函数上下文(this)
- 在使用防抖和节流时,要注意保持函数的this上下文
- 使用apply/call/bind来确保this指向正确
5. 传递参数
- 确保防抖和节流函数能够正确传递参数
- 在实现时要考虑到不定参数的情况
📝 总结
防抖和节流是前端开发中非常重要的性能优化手段,它们可以有效地控制函数的执行频率,提升页面性能和用户体验。
通过本文的介绍,我们学习了:
- 基础概念:防抖(延迟执行)和节流(限制执行频率)
- 手写实现:掌握了防抖和节流的核心算法
- 框架应用:在React和Vue 3中如何使用自定义Hook/Composable实现
- 最佳实践:如何根据场景选择合适的方法,以及使用时的注意事项
希望这个小技巧对你有所帮助!下次遇到高频触发的事件时,不妨试试防抖或节流吧~🌸
相关资源:
标签: #前端性能优化 #防抖 #节流 #React #Vue3