一、前言:为什么需要防抖节流?
在前端开发中,高频触发的事件(如滚动、输入、点击等)容易导致性能问题。防抖(debounce) 和 节流(throttle) 是两种常用的优化手段:
- 防抖:事件触发后等待指定时间再执行,若期间重复触发则重新计时
- 节流:事件触发后立即执行,并在指定时间内不再响应新触发
本文将教你如何在 Vue3 + TypeScript 项目中封装一个灵活可复用的防抖/节流自定义指令。
二、实现思路分析
1. Vue3 自定义指令基础
Vue3 提供了 app.directive()
方法注册全局指令,生命周期包含:
mounted
:元素挂载时updated
:元素更新时unmounted
:元素卸载时
2. 设计目标
- 支持防抖/节流模式切换
- 可自定义延迟时间
- TypeScript 类型支持
- 自动清除事件监听
三、完整代码实现
1. 创建指令文件 directives/debounceThrottle.ts
import type { App, Directive, DirectiveBinding } from 'vue'
type ExecutableFunction = (...args: any[]) => void
type Strategy = 'debounce' | 'throttle'
interface DirectiveOptions {
strategy?: Strategy
delay?: number
}
// 策略模式实现
const strategyImplement = {
debounce(fn: ExecutableFunction, delay: number) {
let timer: NodeJS.Timeout | null = null
return (...args: any[]) => {
if (timer) clearTimeout(timer)
timer = setTimeout(() => fn(...args), delay)
}
},
throttle(fn: ExecutableFunction, delay: number) {
let lastExecTime = 0
return (...args: any[]) => {
const now = Date.now()
if (now - lastExecTime >= delay) {
fn(...args)
lastExecTime = now
}
}
}
}
const debounceThrottleDirective: Directive = {
mounted(el: HTMLElement, binding: DirectiveBinding<ExecutableFunction>) {
const { value: fn } = binding
const { strategy = 'debounce', delay = 300 }: DirectiveOptions = binding.modifiers || {}
if (typeof fn !== 'function') {
throw new Error('v-debounce-throttle requires a function as the value')
}
// 通过策略模式选择实现
const executor = strategyImplement[strategy](fn, delay)
// 存储到元素属性以便后续更新和卸载
el._debounceThrottleHandler = executor
el.addEventListener('click', executor)
},
updated(el, binding) {
// 当参数变化时重新绑定
el.removeEventListener('click', el._debounceThrottleHandler)
debounceThrottleDirective.mounted(el, binding)
},
unmounted(el) {
el.removeEventListener('click', el._debounceThrottleHandler)
delete el._debounceThrottleHandler
}
}
// 扩展HTMLElement类型声明
declare global {
interface HTMLElement {
_debounceThrottleHandler?: (...args: any[]) => void
}
}
export function setupDebounceThrottleDirective(app: App) {
app.directive('debounce-throttle', debounceThrottleDirective)
}
2. 在 main.ts 中注册
import { createApp } from 'vue'
import App from './App.vue'
import { setupDebounceThrottleDirective } from './directives/debounceThrottle'
const app = createApp(App)
setupDebounceThrottleDirective(app)
app.mount('#app')
四、使用示例
1. 基础用法(默认防抖300ms)
<template>
<button v-debounce-throttle="handleClick">提交</button>
</template>
2. 指定节流模式
<button v-debounce-throttle.throttle="handleScroll">滚动处理</button>
3. 自定义延迟时间
<input
v-debounce-throttle:input.throttle="handleInput"
@input="(e) => $emit('update:modelValue', e.target.value)"
/>
五、关键实现解析
- 策略模式:通过对象字面量实现不同策略的快速切换
- 类型安全:使用TS接口规范参数类型
- 内存管理:在unmounted阶段自动移除监听
- 参数更新处理:updated生命周期实现动态参数更新
- 元素属性扩展:通过HTMLElement接口扩展存储处理方法
六、常见应用场景
- 搜索框输入联想(防抖)
- 防止按钮重复点击(节流)
- 滚动事件处理(节流)
- 窗口resize事件(防抖)
- canvas绘图高频事件(节流)
七、总结
通过封装这个自定义指令,我们实现了:
- ✅ 统一的防抖/节流处理逻辑
- ✅ 灵活的策略和参数配置
- ✅ 完善的TS类型支持
- ✅ 自动化的内存管理
在项目中合理使用可以有效提升应用性能,同时保持代码的整洁和可维护性。后续可以继续扩展支持更多事件类型和配置项,打造更强大的指令库。