播放器項目中歌曲搜素頁面的
首先需要在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(); }); }
效果
點擊后全部清空
到此,搜索歷史的功能也就做完了