笔者vue项目有一个需求,搜索框添加历史搜索记录。想着很久没更新博客了,记录一下吧。 PS:pinia+vue3+vant+ts,或许你在使用vue2的语法,不要紧,可以根据自己的需求简单改改。
效果图
正文
搜索框的逻辑就不介绍了,今天这个问题其实是vue操作localStorage。笔者先写了一个工具类来完成对缓存的操作:
export class CacheManager {
static getHomeSearchHistoryWords() {
return localStorage.getItem("home-search-history-words")?.split(",") ?? [];
}
static setHomeSearchHistoryWords(words: string) {
return localStorage.setItem("home-search-history-words", words);
}
static clearHomeSearchHistoryWords() {
localStorage.removeItem("home-search-history-words");
}
}
- 双问号的作用是左表达式为空或undefined时,会取右表达式的值
- getHomeSearchHistoryWords 为读取历史搜索记录,返回值为数组;setHomeSearchHistoryWords 为写入,参数为String类型;clearHomeSearchHistoryWords 为清空。
html如下
<div class="history-words" v-if="words.length">
<div class="history-words-header">
<h4>历史搜索</h4>
<span class="clear" @click="searchStore.clearWords">清空</span>
</div>
<Tag
v-for="item in words"
style="margin-left: 10px"
@click="searchStore.onTagClick(item)"
>{{ item }}</Tag
>
</div>
- Tag 标签是vant中的组件
- searchStore为pinia定义的状态管理容器
- words为pinia中的状态,即历史搜索记录
pinia代码如下
export const HomeSearchStore = defineStore("home-search", {
state: () => ({
query: "",
list: [],
words: [] as string[],
}),
actions: {
search(pushToWords = true) {
if (pushToWords) this.pushWord(this.query);
},
onTagClick(item: string) {
this.query = item;
this.search(false);
},
pushWord(word: string) {
this.words.unshift(word);
this.words = Array.from(new Set(this.words)).slice(0, 10);
CacheManager.setHomeSearchHistoryWords(this.words.join(","));
},
setWords(words: string[]) {
this.words = CacheManager.getHomeSearchHistoryWords();
},
clearWords() {
this.words = [];
CacheManager.clearHomeSearchHistoryWords();
},
},
});
- 请注意对应html与pinia中函数的调用关系
- 在其中定义的状态,如 words,在vue2写法就是你data函数返回对象中的一员;其中定义的函数,则在你methods中定义
- search 函数,你的搜索框发起请求所调用的函数,它的参数 pushToWords(是否存入历史搜索记录)是因为我的需求:点击历史搜索记录或热门标签所发起的请求不会把标签中的内容存入历史搜索记录中(换句话说,只有搜索框中输入内容,然后点击搜索的,才会存入) 而决定的。如果你没有此类需求,可以去掉。
- onTagClick 如上
- pushWord 将此次搜索的内容存入words,首先使用 js数组方法 unshift 将 内容存放在words第一位。然后使用Set方法进行数组去重。这样的好处是,发生重复时,会将与以前那一个重复词语删除,会保证顺序性。最后将数组转为字符串存入缓存。
- setWords 这个函数会在 onMounted 函数中调用,用于在进入页面时,读取缓存中的历史搜索记录
- clearWords 清空
到这里其实就完成了。
遇到的问题
笔者刚开始的时候,并不是这样写的。起初没有定义words变量,在html中的所有操作都是直接调用工具类中的函数,也就意味着直接调用缓存的数据。测试时,笔者发现,输入框输入内容后搜索,历史搜索记录正确更新。但是点击清空却没有任何反应,刷新页面才会生效。 为什么一些内容好用,而一些内容不好用呢?在调查过后,找到了原因
- vue驱动页面更新的是数据,在搜索框输入内容后搜索,笔者所用代码会走网络请求,这个请求在成功时会返回一个数组,我会把这个数组赋值给 list 变量,在页面我使用到 list 来渲染搜索结果的列表。每一次搜索,都会触发数据变化,进而触发页面更新,进而重新调用 getHomeSearchHistoryWords。
- 而清空操作,没有走网络请求,也就意味着数据没有发生变化,页面也没有更新,于是就没有调用 getHomeSearchHistoryWords。
- 解决方法就像以上的代码,自己维护一个 words 变量,保证响应式更新。
结束
如果对你有帮助的话,请点一个👍🏻吧~
|