一介绍
防抖与节流,应用场景有很多,例如:禁止重复提交数据的场景、搜索框输入搜索条件,待输入停止后再开始搜索。
防抖
点击button按钮,设置定时器,在规定的时间内再次点击会重置定时器重新计时,在规定的时间没有再次点击才执行相关函数。
规定时间内最后一次点击有效。
节流
点击button按钮,执行相关函数。设置定时器,在规定的时间内连续点击均无效,定时器过后,可以再次点击。
规定时间内可点击一次(第一次点击有效)。
案例-防抖
Vue项目中使用自定义指令实现(按钮)防抖功能。
应用场景:搜索框输入搜索条件,待输入停止后再开始搜索。
案例-节流
Vue项目中使用自定义指令实现(按钮)节流功能。
应用场景:click事件,禁止重复提交数据的场景。
二 使用
debounceThrottle.ts
import { App, Directive } from "vue"
// 防抖指令
export const debounce: Directive = {
mounted(el: HTMLElement, binding) {
if (!validateBinding(binding, "debounce")) return
let timer: number | null = null
const eventType = binding.arg || "click"
const delay = binding.value.time || 300
const handler = binding.value.handler
const args = binding.value.args || [] // 参数数组
const listener = () => {
if (timer) clearTimeout(timer)
timer = window.setTimeout(() => {
handler(...args) // 执行方法并传递参数
}, delay)
}
el.addEventListener(eventType, listener)
;(el as any).__debounce_listener__ = listener // 保存监听器以便卸载时移除
},
unmounted(el: HTMLElement) {
removeListener(el, "__debounce_listener__", "debounce")
}
}
// 节流指令
export const throttle: Directive = {
mounted(el: HTMLElement, binding) {
if (!validateBinding(binding, "throttle")) return
let lastTime = 0
const eventType = binding.arg || "click"
const delay = binding.value.time || 300
const handler = binding.value.handler
const args = binding.value.args || [] // 参数数组
const listener = () => {
const now = Date.now()
if (now - lastTime >= delay) {
handler(...args) // 执行方法并传递参数
lastTime = now
}
}
el.addEventListener(eventType, listener)
;(el as any).__throttle_listener__ = listener // 保存监听器以便卸载时移除
},
unmounted(el: HTMLElement) {
removeListener(el, "__throttle_listener__", "throttle")
}
}
// 校验绑定值
function validateBinding(binding: any, directiveName: string): boolean {
if (
!binding.value ||
typeof binding.value.handler !== "function" ||
(binding.value.args && !Array.isArray(binding.value.args))
) {
console.warn(
`[v-${directiveName}]: Expected an object with a "handler" function, optional "time" (number), and optional "args" (array). Example: { time: 300, handler: () => {...}, args: [arg1, arg2] }`
)
return false
}
return true
}
// 移除事件监听器
function removeListener(el: HTMLElement, listenerKey: string, directiveName: string) {
const eventType = el.getAttribute(`v-${directiveName}-event`) || "click"
const listener = (el as any)[listenerKey]
if (listener) {
el.removeEventListener(eventType, listener)
}
}
index.ts
import { debounce, throttle } from "./debounceThrottle"
export default {
debounce,
throttle,
}
main.ts
import { createApp } from "vue"
import App from "./App.vue"
import directives from "@/directives"
const app = createApp(App)
// 批量注册自定义指令
Object.keys(directives).forEach((directiveName) => {
app.directive(directiveName, directives[directiveName])
})
使用
<el-button
type="primary"
v-if="hasAuth"
v-throttle:click="{ time: 500, handler: onSaveAndAuth }"
>保存并验证</el-button
>
const onSaveAndAuth = () => {
contactsFormRef.value?.validate((valid) => {
valid && saveContact(1)
})
}