防抖机制
约 1512 字大约 5 分钟
2025-08-16
概述
防抖(Debounce)是一种性能优化技术,通过延迟函数执行来减少不必要的计算和API调用。Evoliant 前端实现了一套完整的防抖机制,适用于搜索、表单验证、API调用等多种场景。
核心组合式函数
useDebounce
基础防抖函数,用于任意函数的防抖处理。
import { useDebounce } from '@/composables/useDebounce'
// 基础用法
const searchAPI = (query: string) => console.log('Searching:', query)
const debouncedSearch = useDebounce(searchAPI, { delay: 500 })
// 调用防抖函数
debouncedSearch('typescript')
// 立即执行
debouncedSearch.flush()
// 取消等待
debouncedSearch.cancel()
// 检查是否正在等待
console.log(debouncedSearch.isPending.value)配置选项
interface DebounceOptions {
delay?: number // 延迟时间(毫秒),默认300ms
immediate?: boolean // 是否在首次调用时立即执行,默认false
maxWait?: number // 最大等待时间,防止长时间不执行,默认为delay的3倍
}返回值
interface DebouncedFunction<T> {
(...args: Parameters<T>): void // 防抖后的函数
flush: () => void // 立即执行函数
cancel: () => void // 取消当前的防抖等待
isPending: Ref<boolean> // 当前是否正在等待执行
}useDebouncedWatch
响应式值的防抖监听,用于监听响应式数据的变化。
import { useDebouncedWatch } from '@/composables/useDebounce'
const searchQuery = ref('')
const { stop, flush, cancel, isPending } = useDebouncedWatch(
searchQuery,
(newValue, oldValue) => {
console.log('Search query changed:', newValue)
// 执行搜索逻辑
},
{ delay: 500 }
)
// 停止监听
stop()
// 立即执行当前等待的函数
flush()useDebouncedRef
表单输入的防抖处理,专门为搜索和表单场景优化。
import { useDebouncedRef } from '@/composables/useDebounce'
// 搜索框场景
const { value: searchValue, flush, cancel, isPending } = useDebouncedRef(
'',
(query) => {
if (query.trim()) {
performSearch(query)
}
},
{ delay: 300 }
)
// 在模板中使用
// <input v-model="searchValue" placeholder="搜索..." />useDebouncedAsync
异步函数的防抖处理,专门用于处理API调用等异步操作。
import { useDebouncedAsync } from '@/composables/useDebounce'
// API调用场景
const searchAPI = async (query: string) => {
const response = await fetch(`/api/search?q=${query}`)
return response.json()
}
const { execute, cancel, isPending, isLoading, error, data } = useDebouncedAsync(
searchAPI,
{ delay: 500 }
)
// 执行搜索
const results = await execute('typescript')
// 检查状态
console.log('Pending:', isPending.value) // 是否等待执行
console.log('Loading:', isLoading.value) // 是否正在加载
console.log('Error:', error.value) // 错误信息
console.log('Data:', data.value) // 最后一次成功的结果实际应用场景
1. 搜索框防抖
在课程搜索页面中的应用:
<script setup lang="ts">
import { useDebouncedRef } from '@/composables/useDebounce'
// 搜索防抖
const { value: searchQuery } = useDebouncedRef('', () => {
// 搜索防抖回调,可以添加搜索埋点等逻辑
}, { delay: 300 })
// 过滤计算属性会自动响应防抖后的搜索值
const filteredCourses = computed(() => {
if (!searchQuery.value.trim()) return courses.value
const query = searchQuery.value.toLowerCase().trim()
return courses.value.filter(course =>
course.name.toLowerCase().includes(query) ||
course.code.toLowerCase().includes(query)
)
})
</script>
<template>
<div class="relative">
<Search class="absolute left-2 top-2.5 h-4 w-4 text-muted-foreground" />
<Input
v-model="searchQuery"
placeholder="搜索课程名称或代码..."
class="pl-8"
/>
</div>
</template>2. 筛选器防抖
为下拉选择器添加防抖监听:
<script setup lang="ts">
import { useDebouncedWatch } from '@/composables/useDebounce'
const selectedSubject = ref('all')
const difficultyFilter = ref('all')
// 为筛选器变化添加防抖监听
useDebouncedWatch(selectedSubject, (newValue) => {
// 学科筛选变化的回调,可以添加分析埋点
console.log('Subject filter changed:', newValue)
}, { delay: 200 })
useDebouncedWatch(difficultyFilter, (newValue) => {
// 难度筛选变化的回调,可以添加分析埋点
console.log('Difficulty filter changed:', newValue)
}, { delay: 200 })
</script>3. API 调用防抖
防抖的异步搜索:
<script setup lang="ts">
import { useDebouncedAsync } from '@/composables/useDebounce'
// 定义搜索API
const searchCourses = async (query: string) => {
const supabase = useSupabaseClient()
const { data, error } = await supabase
.from('courses')
.select('*')
.ilike('name', `%${query}%`)
.limit(10)
if (error) throw error
return data
}
// 使用防抖异步函数
const {
execute: debouncedSearch,
isLoading,
error,
data: searchResults
} = useDebouncedAsync(searchCourses, { delay: 500 })
const searchQuery = ref('')
// 监听搜索输入
watch(searchQuery, (newQuery) => {
if (newQuery.trim()) {
debouncedSearch(newQuery)
}
})
</script>
<template>
<div>
<Input v-model="searchQuery" placeholder="搜索课程..." />
<div v-if="isLoading" class="loading">搜索中...</div>
<div v-else-if="error" class="error">{{ error.message }}</div>
<div v-else-if="searchResults" class="results">
<div v-for="course in searchResults" :key="course.id">
{{ course.name }}
</div>
</div>
</div>
</template>预设配置
防抖场景枚举
export enum DebounceScenario {
SEARCH = 'search', // 搜索输入
FORM_VALIDATION = 'form_validation', // 表单验证
AUTO_SAVE = 'auto_save', // 自动保存
API_CALL = 'api_call', // API调用
SCROLL = 'scroll', // 滚动事件
RESIZE = 'resize', // 窗口大小调整
BUTTON_CLICK = 'button_click' // 按钮点击(防止重复提交)
}预设配置示例
// 不同场景的推荐配置
const presetConfigs = {
[DebounceScenario.SEARCH]: { delay: 300, immediate: false },
[DebounceScenario.FORM_VALIDATION]: { delay: 500, immediate: false },
[DebounceScenario.AUTO_SAVE]: { delay: 2000, maxWait: 10000 },
[DebounceScenario.API_CALL]: { delay: 500, maxWait: 2000 },
[DebounceScenario.SCROLL]: { delay: 100, immediate: false },
[DebounceScenario.RESIZE]: { delay: 250, immediate: false },
[DebounceScenario.BUTTON_CLICK]: { delay: 1000, immediate: true }
}性能优化
1. 计算性能监控
在开发环境中添加性能监控:
const filteredCourses = computed(() => {
const startTime = performance.now()
// 执行过滤逻辑
let filtered = courses.value
// ... 过滤代码
// 性能监控(开发环境)
if (process.env.NODE_ENV === 'development') {
const endTime = performance.now()
if (endTime - startTime > 10) {
console.warn(`课程筛选耗时: ${(endTime - startTime).toFixed(2)}ms`)
}
}
return filtered
})2. 内存优化
确保在组件卸载时清理防抖函数:
// 防抖函数会在组件卸载时自动清理
// 如需手动清理,可以调用:
onUnmounted(() => {
debouncedFunction.cancel()
})最佳实践
1. 选择合适的延迟时间
- 搜索输入: 300ms - 平衡响应性和性能
- 表单验证: 500ms - 给用户足够时间输入
- API调用: 500ms - 减少服务器压力
- 自动保存: 2000ms - 避免频繁保存
2. 合理使用immediate选项
// 搜索场景 - 不立即执行
const searchDebounce = useDebounce(search, {
delay: 300,
immediate: false
})
// 按钮防重复点击 - 立即执行第一次
const submitDebounce = useDebounce(submit, {
delay: 1000,
immediate: true
})3. 设置maxWait防止长时间不执行
// 自动保存场景
const autoSave = useDebounce(saveData, {
delay: 2000,
maxWait: 10000 // 最多10秒必须执行一次
})4. 错误处理
const { execute, error } = useDebouncedAsync(apiCall, { delay: 500 })
// 监听错误
watch(error, (err) => {
if (err) {
toast.error(`操作失败: ${err.message}`)
}
})类型定义
完整的类型定义请参考 @/types/debounce.ts 文件,包含:
DebounceOptions- 防抖配置选项DebouncedFunction<T>- 防抖函数返回类型DebouncedWatcher- 防抖监听器类型DebouncedRef<T>- 防抖响应式值类型DebouncedAsync<T>- 防抖异步函数类型
调试技巧
1. 开发环境日志
const debouncedSearch = useDebounce((query: string) => {
console.log(`[DEBOUNCE] 执行搜索: ${query}`)
performSearch(query)
}, { delay: 300 })2. 防抖状态监控
<template>
<div>
<Input v-model="searchQuery" />
<div v-if="isPending" class="text-muted-foreground">
搜索准备中...
</div>
</div>
</template>3. 性能分析
使用浏览器开发工具的Performance面板分析防抖效果对页面性能的改善。
版权所有
版权归属:Evoliant
许可证:MIT