需手动实现防抖、竞态控制与定时器清理:输入防抖用 clearTimeout+setTimeout,页面卸载前清除定时器;联想面板用v-if而非v-show;空输入、请求中、失败状态需分别处理;历史记录限长去重;远程请求用AbortController拦截竞态。

直接用 uni-search-bar 就能做出可用的搜索栏,但不加控制会立刻崩——输“北京”触发 4 次请求、联想词跳变、iOS 键盘收不起、历史记录越存越多卡 UI,这些都不是配置问题,是默认行为没兜住。
防抖必须手写,不能靠 @confirm 或 watch
uni-search-bar 的 @input 是字符级触发,不是用户停顿后才发。不加防抖,连打就狂发请求,后端扛不住,前端联想列表也乱序。
- 在
data里声明searchTimer: null -
handleInput(val)中先clearTimeout(this.searchTimer),再this.searchTimer = setTimeout(() => { /* 请求逻辑 */ }, 300) - 页面卸载前(
onUnload或beforeDestroy)必须clearTimeout(this.searchTimer),否则内存泄漏 - 别把防抖塞进
@confirm:那是回车/点击搜索按钮时才触发,和输入联想无关
v-if 控制联想面板,别用 v-show
App 端(尤其 iOS)下拉联想面板若用 v-show,DOM 节点还在,容易遮挡底部按钮、滚动错位、键盘收不掉。
- 用
v-if="showSuggestions && suggestionList.length",真销毁节点 - 空输入、请求中、失败状态要分开判断:
loading显示转圈,失败留空或提示“暂无建议”,不能只看数组长度 - 点击联想项后立即调用
searchBarRef.blur(),否则 iOS 键盘大概率卡住
关键词长度和请求竞态必须拦截
搜单字(如“手”)返回几百条结果,既无业务意义,又浪费资源;用户快速删改时,后发请求可能先返回,覆盖正确结果。
- 防抖回调里加校验:
if (val.length - 用户清空输入框时,主动取消 pending 请求(如
controller.abort()),不发空字符串请求 - 远程请求用
AbortController,每次新请求前 abort 上一个 - 本地历史记录要用
uni.setStorage/uni.getStorage,别碰localStorage(App 和小程序不兼容) - 历史数组要限长:
history = history.slice(-10),新增前先去重:history = [...new Set([...history, keyword])]
最常被忽略的是「请求竞态」和「页面卸载未清理定时器」——前者让联想词错乱,后者让组件残留监听,长期运行的 App 容易越来越卡。这两个点不处理,其他都白搭。











