存数组必须先 JSON.stringify,读取时需兜底空字符串再 JSON.parse;key 统一小写+下划线;去重置顶用 findIndex+splice;onShow 同步历史更可靠;清空需 setStorageSync 空数组并 forceUpdate。

直接用 uni.setStorageSync 存数组会静默失败,必须先 JSON.stringify;读取时若不兜底空字符串,JSON.parse(null) 会报错崩溃。
存数组前必须 JSON.stringify,否则读出来是 undefined
uni.setStorageSync 只接受字符串作为 value,传数组进去不报错但实际没写入。你调 this.historyList.unshift('苹果') 看似更新了数组,但没转成字符串就存,下次 uni.getStorageSync 拿到的是 undefined 或空字符串。
- ✅ 正确写法:
uni.setStorageSync('search_history', JSON.stringify(this.historyList)) - ❌ 错误写法:
uni.setStorageSync('search_history', this.historyList) - 读取时必须兜底:
const list = JSON.parse(uni.getStorageSync('search_history') || '[]'),否则JSON.parse(undefined)直接崩 - key 名统一用小写+下划线:
search_history,大小写或空格差异会导致 H5 和小程序读不到同一份数据
去重 + 置顶不能只 unshift,得先 findIndex 再 splice
用户搜 “iPhone” → “Android” → 再搜 “iPhone”,理想效果是 “iPhone” 回到第一位,“Android” 往后排。如果每次无脑 unshift,就会重复。
- 用
findIndex查旧位置:const idx = this.historyList.findIndex(item => item.toLowerCase() === keyword.toLowerCase()) - 查到了就
splice(idx, 1)删除旧记录,再unshift(keyword)插入开头 - 别用
filter或Array.from(new Set()):前者破坏时间顺序,后者打乱原有先后 - 最后截断:
this.historyList = this.historyList.slice(0, 10),避免无限膨胀
onShow 比 onLoad 更可靠地同步历史列表
onLoad 只在页面首次加载触发,而搜索页常被反复进出(比如从结果页返回),这时缓存可能已被清空或更新,onLoad 不会再次执行。
- ✅ 推荐在
onShow中读取并赋值:this.historyList = JSON.parse(uni.getStorageSync('search_history') || '[]') - ⚠️ 如果页面带默认关键词等参数,可在
onLoad初始化,onShow负责同步缓存状态 - 安卓低端机上
uni.getStorageSync可能抛错,建议包一层try/catch并 fallback 到空数组 - 别在
@input里频繁读写——每敲一个字都触发 I/O,低端机卡顿,还可能因切后台丢数据
清空历史必须 setStorageSync 空数组,不是 removeStorageSync
只调 uni.removeStorageSync('search_history') 是不够的:后续 JSON.parse(uni.getStorageSync('search_history')) 会因返回 null 报错;而且 Vue2 在 H5/APP 下对空数组响应不敏感,UI 常卡住不动。
- ✅ 安全清空两步走:
this.historyList = []→uni.setStorageSync('search_history', JSON.stringify([])) - 清空后必须手动触发
this.$forceUpdate()(尤其 H5/APP 环境),否则视图不刷新 - 如果用了
computed包装历史列表,记得清空后也手动赋值,否则 getter 仍返回旧引用 - 点击历史项后,要
uni.hideKeyboard()收起键盘,否则 iOS App 下容易卡在输入态
最易被忽略的是大小写归一化比对和读取时的默认值兜底——它们决定了历史是否真正“按用户直觉”工作,而不是靠运气跑通。


















