vue项目搜索历史功能的实现
播放器项目中歌曲搜素页面的
首先需要在state定义搜索历史,在其中保存搜索历史
state.js:
// 搜索历史: searchHistory: []
mutations中新增改变搜索历史的方法
mutations.js:
SET_SEARCH_HISTORY(state, history) { state.searchHistory = history }
在actions中需要提交一个状态改变history,由于需要存储到vuex中
export const saveSearchHistory = function ({ commit }, query) { commit('SET_SEARCH_HISTORY', saveSearch(query)) }
在utils中新建一个cache.js,用来存储localstroage.js,首先安装good-stroage作为存储缓存的依赖
cache.js:
import storage from 'good-storage';
定义一个key代表存储搜索历史:
const SEARCH_KEY = "__search__" // 只缓存十五条数据 const SEARCH_MAX_LEN = 15
实现逻辑:
// arr总数组,val插入的数组,maxlen总条数 function insertArray(arr, val, maxLen) { const index = _findIndex(arr, val); // 如果搜到第一条数据返回即可 if (index === 0) { return } // 如果数组中有这条数据,还不是在最前面,则删除之前的数据 if (index > 0) { arr.splice(index, 1); console.log('删除已存在的数据') } // 把当前的插入最前面 arr.unshift(val) // 当存储的长度大于设置的长度则移除最后一条: if (maxLen && arr.length > maxLen) { arr.pop() } } export function saveSearch(query) { // 获取当前,如果没有存过则是空数组: let searches = storage.get(SEARCH_KEY, []) insertArray(searches, query, SEARCH_MAX_LEN); storage.set(SEARCH_KEY, searches); return searches } // 遍历数组对象,找到插入值的索引(这个方法其实不太好,其实就是把对象转换成字符串后向后不断对比,应该根据id来判断是否一致) function _findIndex(l, o) { var objStr = JSON.stringify(o) return l.reduce((index, ele, i) => { if (JSON.stringify(ele) === objStr) { return i } else { return index } }, -1) }
在搜素结果的组件中,给每一项添加一个点击事件,传递值,并让父组件监听:
selectItem(item) { this.$emit("select", item); },
父组件中监听,并使用mapActions提交变化
methods: { async _getSearchHot() { const { data: res } = await getSearchHot(); this.hotSearchList = res.slice(0, 8); console.log(this.hotSearchList); }, ...mapActions(["saveSearchHistory"]), onSaveSearch(item) { this.saveSearchHistory(item); } }
在浏览器中测试,分别输入一首海阔天空和光辉岁月,可以看到前一步提交和后一步提交的状态
这时候再搜索第一次搜索的海阔天空,会把之前的删除,并新增一条海阔天空插入到数组最前面
在浏览器的控制台中的Application也可以查看到localstroage的变化
接下来就是定义一个基础组件,把所有历史记录使用mapGetters获取,并循环遍历
<template> <div class="search-list" v-show="searches.length"> <ul> <li v-for="(item,index) in searches" :key="index" > {{item.name}} <i class="iconfont icondel" ></i> </li> </ul> </div> </template>
到此页面也渲染完成了
接下来实现点击播放历史记录中的歌曲,和删除历史记录中的歌曲,因为是基础组件,所以向外暴露出事件让父组件监听
<li v-for="(item,index) in searches" :key="index" @click="selectItem(item)"> {{item.name}} <i class="iconfont icondel" @click.stop="deleteTag(item)"></i> </li>
methods: { selectItem(item) { this.$emit("select", item); }, deleteTag(item) { this.$emit("delete", item); } }
父组件中监听
<search-list :searches="searchHistory" @select="selectItem" @delete="deleteSearch"></search-list>
播放点击的歌曲很简单,使用之前定义的mapactions中的insertSong即可,之前歌曲的数据也都一一传入
selectItem(item) { this.insertSong(item); },
删除播放的历史记录中的歌曲,需要 定义一个新的actions,把之前的history重新删掉选中项后再返回结果
export const deleteSearchHistory = function ({ commit }, query) { commit('SET_SEARCH_HISTORY', deleteSearch(query)) }
在cache.js中新增方法
// 从数组删除方法: function deleteFromArray(arr, val) { const index = _findIndex(arr, val); if (index > -1) { arr.splice(index, 1) } } // 删除搜索历史的某一项: export function deleteSearch(query) { let searches = storage.get(SEARCH_KEY, []); deleteFromArray(searches, query); // 删除完再保存数组: storage.set(SEARCH_KEY, searches); // 同时返回被删除的数组 return searches }
最后在父组件中定义子组件的删除事件中调用actions并传入item即可
deleteSearch(item) { this.deleteSearchHistory(item); }
点击后测试效果,可以发现已经成功了
最后实现点击垃圾清空全部历史记录的功能,这个就很简单了
一样的方法定义一个actions
// 点击垃圾桶,删除全部历史记录 export const clearSearchHistory = function ({ commit }) { commit('SET_SEARCH_HISTORY', clearSearch()) }
cache.js新增一个清除全部的函数
export function clearSearch() { storage.remove(SEARCH_KEY) return [] }
点击时候调用即可,在这里用了vant的dialog组件
deleteAllHis() { Dialog.confirm({ message: "是否清空历史记录?", className: "dialog_themedark", confirmButtonText: "清空", width: "250px" // transition: "zoomIn" }).then(() => { this.clearSearchHistory(); }); }
效果
点击后全部清空
到此,搜索历史的功能也就做完了