搜索框防抖 + 竞态完整总结

📅 2026/7/3 1:17:32
搜索框防抖 + 竞态完整总结
第一层防抖原理与原生实现1. 核心逻辑防抖依靠闭包缓存定时器 timer用户连续输入时每次清空上一次延时、重新计时仅停止输入延迟 300ms 后发起搜索大幅减少 Axios 请求次数。必须保留 this 上下文、透传参数局限性只控制请求触发频率无法解决网络延迟带来的竞态问题。基础防抖工具函数// 通用防抖可在Vue全局导入使用 export function debounce(fn, delay 300) { let timer null return function(...args) { clearTimeout(timer) timer setTimeout(() fn.apply(this, args), delay) } }第二层竞态问题 Axios 两种解决方案1. 竞态成因用户先后输入a、ab同时发起两条 Axios 请求网络波动下短关键词请求响应更慢后发起的请求先返回旧数据覆盖最新搜索结果列表渲染错乱。 不推荐 loading 锁会阻塞连续输入交互很差。方案 1版本序列号标记兼容所有 Axios 版本每次搜索自增序号接口回调判断当前序号是否为最新过期响应直接丢弃不更新页面数据。let latestSeq 0 const search debounce(async (keyword) { const curSeq latestSeq const res await axios.get(/api/search, { params: { keyword } }) // 过期请求抛弃结果 if (curSeq ! latestSeq) return searchList.value res.data })优点无版本限制缺点无效请求仍会走完网络流程浪费带宽。方案 2AbortController 取消请求Axios 0.22 推荐Axios 支持signal中断信号每次新搜索直接终止上一个未完成请求从根源杜绝过期响应。let controller null const search debounce(async (keyword) { // 取消上一轮未完成请求 controller?.abort() controller new AbortController() try { const res await axios.get(/api/search, { params: { keyword }, signal: controller.signal }) searchList.value res.data } catch (err) { // 主动取消的请求不打印错误 if (!axios.isCancel(err)) console.error(搜索失败, err) } })优点直接中断网络减少服务器压力缺点仅支持 Axios0.22 以上版本。第三层Vue3 工程化封装组合式 useDebounceSearch 工具函数Vue3 中使用ref/shallowRef存储定时器与 AbortController 实例封装成组合式函数 (composable)代替 React Hook自动处理组件卸载资源清理防止内存泄漏。完整 composable 代码useDebounceSearch.jsimport { shallowRef, onUnmounted, useCallback } from vue import axios from axios export function useDebounceSearch(delay 300) { // shallowRef 存储复杂实例不触发多余响应式更新 const timer shallowRef(null) const abortCtrl shallowRef(null) // 防抖搜索处理函数 const handleSearch useCallback(async (keyword, setResult) { // 清除旧定时器 if (timer.value) clearTimeout(timer.value) timer.value setTimeout(async () { // 终止上一次未完成请求 abortCtrl.value?.abort() const controller new AbortController() abortCtrl.value controller try { const res await axios.get(/api/search, { params: { keyword }, signal: controller.signal }) setResult(res.data) } catch (err) { if (!axios.isCancel(err)) console.error(err) } }, delay) }, [delay]) // 组件卸载统一清理定时器、中断请求 onUnmounted(() { timer.value clearTimeout(timer.value) abortCtrl.value?.abort() }) return { handleSearch } }Vue3 组件内使用示例script setup import { ref } from vue import { useDebounceSearch } from /composables/useDebounceSearch const searchList ref([]) const { handleSearch } useDebounceSearch(300) // 输入框绑定 const inputChange (e) { const keyword e.target.value handleSearch(keyword, (data) { searchList.value data }) } /script template input inputinputChange placeholder搜索商品 / ul v-foritem in searchList :keyitem.id{{ item.name }}/ul /template四、项目落地场景商城后台管理系统Vue3 Vite商品列表、订单列表顶部实时搜索框支持名称、编号模糊检索用户快速输入、反复修改筛选条件会并发大量 Axios 请求全局引入封装好的useDebounceSearch组合函数统一防抖延时、请求中断逻辑弹窗 / 页面关闭时自动取消请求避免控制台报错。企业 OA 文档检索平台海量文件实时联想搜索网络波动大项目 Axios 版本 0.26优先使用 AbortController 方案减少无效请求带宽消耗联想列表不会出现旧数据闪烁覆盖问题。移动端 H5 商品搜索页移动端输入频繁、网络不稳定封装组合函数全局复用若项目 Axios 版本过低切换序列号标记方案兜底保证低端机型兼容性。公共搜索组件抽离将带防抖、竞态处理的输入框封装成全局公共组件SearchInput.vue内部直接引入useDebounceSearch业务页面直接引入使用无需重复写防抖和请求中断逻辑。面试口述精简总结三层递进防抖依靠闭包缓存定时器降低输入时 Axios 请求频率但无法解决网络延迟带来的竞态问题竞态是后发请求先返回、旧数据覆盖页面Axios 有两种解决方式一是版本序列号过滤过期响应二是 AbortController 主动取消上一轮请求后者性能更优Vue3 项目里不会零散写逻辑会封装组合式函数 (composable)用 shallowRef 存储定时器和中断控制器借助 onUnmounted 生命周期统一清理资源避免内存泄漏 落地场景主要是后台管理表格检索、H5 商城搜索、文档实时联想根据 Axios 版本选择对应竞态方案抽成公共组合函数 / 组件全局复用。