UNPKG

vite-uni-dev-tool

Version:

vite-uni-dev-tool, debug, uni-app, 一处编写,到处调试

268 lines (228 loc) 7.35 kB
type DebounceOptions = { leading?: boolean; trailing?: boolean; maxWait?: number; // 可选:最大等待时间 }; type DebouncedFunction<T extends (...args: any[]) => any> = (( ...args: Parameters<T> ) => ReturnType<T> | undefined) & { cancel: () => void; flush: () => ReturnType<T> | undefined; }; /** * 创建一个防抖函数,该函数在最后一次调用后等待指定时间才执行。 * @param func 需要防抖的函数 * @param wait 等待时间(毫秒) * @param options 配置选项 * @param options.leading 是否在开始时立即执行 * @param options.trailing 是否在延迟结束后执行最后一次调用 * @returns 防抖后的函数 */ export function debounce<T extends (...args: any[]) => any>( func: T, wait: number, options: DebounceOptions = {}, ): DebouncedFunction<T> { const { leading = false, trailing = true } = options; let timeout: ReturnType<typeof setTimeout> | null = null; let lastArgs: Parameters<T> | null = null; let lastThis: ThisParameterType<T> | null = null; let result: ReturnType<T> | undefined; let lastCallTime: number | null = null; let lastInvokeTime: number = 0; const invokeFunc = (time: number) => { const args = lastArgs; const thisArg = lastThis; lastArgs = lastThis = null; lastInvokeTime = time; result = func.apply(thisArg, args!); return result; }; const leadingEdge = (time: number) => { // 开始新的计时周期 lastInvokeTime = time; // 如果 trailing 为 true,启动定时器执行最后一次调用 if (trailing) { timeout = setTimeout(timerExpired, wait); } // 如果 leading 为 true,立即执行 return leading ? invokeFunc(time) : undefined; }; const trailingEdge = (time: number) => { timeout = null; // 只有在 trailing 为 true 且有等待的调用时才执行 if (trailing && lastArgs) { return invokeFunc(time); } lastArgs = lastThis = null; return result; }; const remainingWait = (time: number) => { const timeSinceLastCall = time - (lastCallTime || 0); const timeSinceLastInvoke = time - lastInvokeTime; const timeWaiting = wait - timeSinceLastCall; // 如果设置了最大等待时间,取最小值 return Math.min(timeWaiting, wait - timeSinceLastInvoke); }; const shouldInvoke = (time: number) => { const timeSinceLastCall = time - (lastCallTime || 0); const timeSinceLastInvoke = time - lastInvokeTime; // 第一次调用 if (lastCallTime === null) { return leading && timeSinceLastInvoke < wait; } // 时间间隔超过等待时间,应该执行 if (timeSinceLastCall >= wait) { return true; } // 如果设置了最大等待时间,并且超过最大等待时间 if (timeSinceLastInvoke >= wait) { return true; } return false; }; const timerExpired = () => { const time = Date.now(); if (shouldInvoke(time)) { trailingEdge(time); } else if (timeout) { // 继续等待 timeout = setTimeout(timerExpired, remainingWait(time)); } }; const debounced = function ( this: ThisParameterType<T>, ...args: Parameters<T> ): ReturnType<T> | undefined { const time = Date.now(); const isInvoking = shouldInvoke(time); lastArgs = args; lastThis = this; lastCallTime = time; if (isInvoking) { if (timeout === null) { // 第一次调用,且 leading 为 true return leadingEdge(time); } // 如果不是第一次调用,但在等待时间后应该执行 else if (timeout) { clearTimeout(timeout); timeout = setTimeout(timerExpired, wait); // 如果 leading 为 true,立即执行 if (leading) { return invokeFunc(time); } } } else { // 没有触发执行条件,设置定时器 if (!timeout) { timeout = setTimeout(timerExpired, wait); } } return result; }; debounced.cancel = (): void => { if (timeout !== null) { clearTimeout(timeout); timeout = null; lastArgs = lastThis = null; lastCallTime = lastInvokeTime = 0; } }; debounced.flush = (): ReturnType<T> | undefined => { if (timeout !== null) { const time = Date.now(); const flushedResult = trailingEdge(time); return flushedResult; } return result; }; return debounced; } type ThrottleOptions = { leading?: boolean; trailing?: boolean; }; /** * 创建一个节流函数,在指定时间间隔内只执行一次原函数 * @param func 需要节流的函数 * @param wait 等待时间(毫秒) * @param options 配置选项 * @param options.leading 是否在开始时立即执行 * @param options.trailing 是否在延迟结束后执行最后一次调用 * @returns 节流后的函数 */ export function throttle<T extends (...args: any[]) => any>( func: T, wait: number, options: ThrottleOptions = {}, ): (...args: Parameters<T>) => ReturnType<T> | undefined { let timeout: ReturnType<typeof setTimeout> | null = null; let lastArgs: Parameters<T> | null = null; let lastThis: any = null; let result: ReturnType<T> | undefined; let lastCallTime: number | null = null; const { leading = true, trailing = true } = options; const invokeFunc = (time: number) => { const args = lastArgs; const thisArg = lastThis; lastArgs = lastThis = null; lastCallTime = time; result = func.apply(thisArg, args!); return result; }; const leadingEdge = (time: number) => { lastCallTime = time; timeout = null; return leading ? invokeFunc(time) : undefined; }; const trailingEdge = (time: number) => { timeout = null; if (lastArgs && trailing) { return invokeFunc(time); } lastArgs = lastThis = null; return result; }; const remainingWait = (time: number) => { if (lastCallTime === null) return wait; const timeSinceLastCall = time - lastCallTime; return wait - timeSinceLastCall; }; const shouldInvoke = (time: number) => { if (lastCallTime === null) return true; const timeSinceLastCall = time - lastCallTime; return timeSinceLastCall >= wait || timeSinceLastCall < 0; }; const timerExpired = () => { const time = Date.now(); if (shouldInvoke(time)) { return trailingEdge(time); } timeout = setTimeout(timerExpired, remainingWait(time)); return undefined; }; const throttled = function (this: any, ...args: Parameters<T>) { const time = Date.now(); const isInvoking = shouldInvoke(time); lastArgs = args; lastThis = this; if (isInvoking) { if (timeout === null) { return leadingEdge(time); } } if (timeout === null) { timeout = setTimeout(timerExpired, wait); } return result; }; throttled.cancel = () => { if (timeout !== null) { clearTimeout(timeout); } lastCallTime = null; timeout = lastArgs = lastThis = null; }; return throttled; }