一、搜素效果如下:

二、核心
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>
