一、搜素效果如下:
二、核心
1)利用oninput屬性來觸發搜素功能
2)利用RegExp來對字符串來全局匹配關鍵字,利用replace方法來對匹配的關鍵字進行嵌入高亮的<span class="gaoliang">標簽,最后利用v-html來嵌入html標簽來達到關鍵字高亮顯示
3)利用axios.CancelToken來終止上一次的異步請求,防止舊關鍵字查詢覆蓋新輸入的關鍵字查詢結果。
三、代碼
1)HTML 搜索框部分綁定input事件(搜索框獨立出來,作為一個基礎組件(叫SearchToolbar.vue),嵌入到KnowledgeSearch.vue中)
<input type="text" class="input-search" placeholder="請輸入搜索內容" v-model.trim="searchKey" @input="searchEvent" ref="searchInput" autocomplete="off" autocapitalize="off" autocorrect="off"/>
2)因為input綁定了輸入監聽事件@input,每一次輸入值的改變,都會觸發方法searchEvent(),尤其是在輸入搜索關鍵字的時候,這種情況必然發生發送多次http異步請求,這樣頻繁地請求會導致流量損耗與性能下降。
如何解決?
我們利用setTimeout與clearTimeout元素,控制輸入間隔為500ms,如果超過500s還沒輸入任何東西,就會向后端發送http異步請求。例如在搜索框種輸入1,然后500ms過去之后,就會發送異步請求,如果在輸入1之后的500ms輸入值,例如我在499ms輸入了值‘2’,那么上一次關鍵字為1的異步請求就會取消,進而進行關鍵字為‘12’的異步請求,並等待500ms。代碼如下:
searchEvent() { this.clearTimer(); if (this.searchKey && this.searchKey.length > 0) { //獲取當前延時函數的ID,便於后面clearTimeout清除該ID對應的延遲函數 this.timer = setTimeout(() => { this.$emit('searchHandler', this.searchKey); }, 500); } else { this.$emit('searchHandler', this.searchKey); } }, clearTimer() { if (this.timer) { clearTimeout(this.timer); } }
注意,你仔細想想,這里還存在一個漏洞。根據關鍵字發送異步http請求,假如在網絡環境並不太好的情況下,異步請求可能經過2秒才返回關鍵字為‘成’的數據給我。然而,我在輸入完關鍵字‘成’,超過500ms之后在輸入‘龍’,那么之前的‘成’關鍵字的http異步請求已經發送給了服務器(要經過2s才返回數據給我。),而關鍵字為‘成龍’的http異步請求也發送過去了,它的響應時間是1s就返回數據。那么經過2s之后,得到的將會是關鍵字為‘成’的數據。因為最新的‘成龍’數據(1s)已經被‘成’數據(2s)所覆蓋了。
3)利用axios.CancelToken來終止上一次的異步請求,防止舊關鍵字查詢覆蓋新輸入的關鍵字查詢結果。
import httpService from '@/services/HttpService'; <script> export default{ data(){ return{ $http: null, CancelToken: null, cancel: null, } }, methods{ queryDataListByKey() { if (this.searchKey.length === 0) { this.loadedData = false; this.dataList = []; return; } let params = { 'pageNo': this.pageNo, 'pageSize': this.pageSize, 'keyWord': this.searchKey }; this.loading(true);
// this.cancel === null,意味着第一次請求 if (this.cancel) {
// 取消上一次的異步請求 this.cancel('canceled by user'); } this.$http.get('rule/findRuleListByKeyWord', {
// 每一次請求關鍵字資源的http異步請求,都要新建一個異步取消操作實例,並賦值給this.cancel cancelToken: new this.CancelToken((c) => { this.cancel = c; }), 'params': params }).then((res) => { this.loading(false); this.loadedData = true; this.processDataList(res); }, (error) => { this.loading(false); this.loadedData = true; this.loadMoreConfig.loading = true; if (error !== null) { this.$vux.toast.show({ text: '數據加載失敗', type: 'text' }); } }); }, } } </script>
4)v-html與Reg為搜索結果的關鍵字高亮顯示
這一步是在展示數據的組件上做的。
4.1)HTML結果數據展示部分
<div class="title-info" v-html="ruleTitle"></div>
4.2)js部分
import httpService from '@/services/HttpService'; <script> export default{ props: { // 每一條帶關鍵字的結果對象,父組件傳過來的 item: {}, }, computed: { ruleTitle() { let titleString = this.item.name; if (!titleString) { return ''; } if (this.searchValue && this.searchValue.length > 0) { // 匹配關鍵字正則 let replaceReg = new RegExp(this.searchValue, 'g'); // 高亮替換v-html值 let replaceString = '<span class="search-text">' + this.searchValue + '</span>'; // 開始替換 titleString = titleString.replace(replaceReg, replaceString); } return titleString; } }, } </script>