// src/hooks/useDebounce.ts import { onUnmounted, Ref } from 'vue'; import { useState } from './useState'; import { useEffect } from './useEffect'; export interface UseDebounceOptions { delay?: number; immediate?: boolean; maxWait?: number; } export function useDebounce(source: Ref, options: number | UseDebounceOptions = {}) { const config = typeof options === 'number' ? { delay: options } : options; const { delay = 300, immediate = false, maxWait } = config; const [debouncedValue, setDebouncedValue] = useState(source.value as T); const [isPending, setIsPending] = useState(false); let timer: any = null; let maxWaitTimer: any = null; let isFirstCall = true; // 标记是否为首次调用,用于 immediate 模式 // 清理 const clearTimers = () => { if (timer) { clearTimeout(timer); timer = null; } if (maxWaitTimer) { clearTimeout(maxWaitTimer); maxWaitTimer = null; } }; // 更新 const updateValue = () => { setDebouncedValue(source.value); setIsPending(false); clearTimers(); // 安静期结束后重置 isFirstCall,使下次触发能立即执行(immediate 模式) isFirstCall = true; }; useEffect(() => { setIsPending(true); // 处理 maxWait if (maxWait && !maxWaitTimer) { maxWaitTimer = setTimeout(() => { updateValue(); }, maxWait); } // 处理 delay if (timer) clearTimeout(timer); if (immediate && isFirstCall) { isFirstCall = false; updateValue(); } else { timer = setTimeout(() => { updateValue(); }, delay); } return clearTimers; }, [source]); // 卸载 onUnmounted(() => { clearTimers(); }); return { debouncedValue, isPending, refresh: updateValue, cancel: clearTimers, }; }