防抖 hook 的核心是解决函数引用稳定、组件卸载安全、参数与 this 正确传递三大问题;react 用 useref + usecallback 实现自动清理,vue 3 借助 ref + onbeforeunmount 并暴露 cancel 方法,均需规避 this 绑定、依赖遗漏、误用 useeffect 等陷阱。

防抖 Hook 的核心设计原则
现代框架(React、Vue)中封装防抖 Hook,关键不是“写个定时器”,而是解决三个实际问题:函数引用稳定、组件卸载安全、参数与 this 上下文正确传递。直接在事件回调里写 setTimeout 容易造成闭包滞留、内存泄漏或重复绑定,而 Hook 封装能天然规避这些。
React 中 useDebounce Hook 的标准写法
用 useRef 存储定时器 ID,用 useCallback 保证返回函数引用不变,同时在依赖变化或组件卸载时自动清理:
- 接收目标函数、延迟毫秒数、可选的依赖数组(如搜索关键词)
- 每次依赖更新时,先清除旧定时器,再设置新定时器
- 不依赖
useEffect做清理——useRef的 timer 在下次调用时自然覆盖,卸载时由 React 自动回收闭包
示例代码:
import { useRef, useCallback } from 'react';
function useDebounce(fn, delay, deps = []) {
const timerRef = useRef(null);
return useCallback((...args) => {
if (timerRef.current) clearTimeout(timerRef.current);
timerRef.current = setTimeout(() => fn(...args), delay);
}, [fn, delay, ...deps]);
}
// 使用
function SearchInput() {
const [query, setQuery] = useState('');
const debouncedSearch = useDebounce(
(q) => q && fetchSuggestions(q),
300,
[query] // 仅当 query 变化时重置防抖逻辑
);
useEffect(() => {
debouncedSearch(query);
}, [query, debouncedSearch]);
return <input value="{query}" onchange="{(e)"> setQuery(e.target.value)} />;
}
Vue 3 中 useDebounce 的 Composition API 实现
利用 ref 管理定时器、onBeforeUnmount 保证清理,并支持响应式参数自动追踪:
- 不把函数作为依赖传入,而是让调用方在 setup 中显式触发,避免无限循环
- 返回一个可调用函数 + 一个 cancel 方法,便于手动控制
- 内部使用
getCurrentInstance()或生命周期钩子确保组件销毁时清除定时器
示例:
import { ref, onBeforeUnmount } from 'vue';
export function useDebounce(callback, delay = 300) {
const timer = ref(null);
const run = (...args) => {
if (timer.value) clearTimeout(timer.value);
timer.value = setTimeout(() => callback(...args), delay);
};
const cancel = () => {
if (timer.value) {
clearTimeout(timer.value);
timer.value = null;
}
};
onBeforeUnmount(cancel);
return { run, cancel };
}
// 组件内使用
export default {
setup() {
const { run: debouncedFetch } = useDebounce(fetchApi, 400);
const search = (text) => debouncedFetch(text);
return { search };
}
};
封装时必须避开的常见陷阱
很多团队自己写的防抖 Hook 运行一段时间后出 bug,往往栽在这几个细节上:
-
没处理 this 绑定:直接传箭头函数或未绑定上下文的普通函数,导致
fn.apply失败;应统一用展开参数fn(...args)或明确要求传 bound 函数 - 忽略依赖数组更新:比如搜索函数依赖某个 token,但没把 token 加入 deps,导致防抖始终调用旧 token 下的请求
- 误用 useEffect 清理定时器:在 effect 里清理,会因依赖变化频繁重设,反而增加开销;应靠函数调用时的 clearTimeout 主动控制
- 没暴露 cancel 方法:表单取消、页面跳转等场景需要手动中断,否则可能触发已销毁组件的 setState 或 update
真正优雅的封装,是让使用者几乎感觉不到“它在防抖”,只关注业务逻辑是否准时、安全、可预测地执行。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











