36 lines
1.1 KiB
TypeScript
36 lines
1.1 KiB
TypeScript
import type { Directive } from 'vue'
|
||
|
||
interface DebounceBinding {
|
||
handler: (...args: unknown[]) => void
|
||
delay?: number
|
||
}
|
||
|
||
type DebounceValue = DebounceBinding['handler'] | DebounceBinding
|
||
|
||
const listenerMap = new WeakMap<Element, EventListener>()
|
||
|
||
function normalize(value: DebounceValue): DebounceBinding {
|
||
return typeof value === 'function' ? { handler: value } : value
|
||
}
|
||
|
||
/** v-debounce 防抖点击:v-debounce="fn" 或 v-debounce="{ handler: fn, delay: 500 }" */
|
||
const debounce: Directive<Element, DebounceValue> = {
|
||
mounted(el, binding) {
|
||
const { handler, delay = 300 } = normalize(binding.value)
|
||
let timer: ReturnType<typeof setTimeout> | null = null
|
||
const listener: EventListener = () => {
|
||
if (timer !== null) clearTimeout(timer)
|
||
timer = setTimeout(() => handler(), delay)
|
||
}
|
||
listenerMap.set(el, listener)
|
||
el.addEventListener('click', listener)
|
||
},
|
||
unmounted(el) {
|
||
const listener = listenerMap.get(el)
|
||
if (listener) el.removeEventListener('click', listener)
|
||
listenerMap.delete(el)
|
||
},
|
||
}
|
||
|
||
export default debounce
|