在如今的互聯網世界裏,自動完成的搜索功能是一個很常見的功能。好比百度、搜狗、360搜索 ...css
功能描述一下大概是這個樣子的:有一個搜索框,用戶在裏面輸入要查詢的條件,系統會「智能」判斷用戶輸完了,而後自動根據條件去搜索相關的數據返回給用戶。vue
網上這個自動完成的插件不少,實現自動完成功能也不復雜,特別是像vue、angularjs、react這類能夠實現雙向綁定的庫出現之後,實現就更方便了。本文不講自動完成功能的實現,而是介紹自動完成功能後續數據的請求該如何考慮,主要要處理下面兩個問題。react
問題1:自動完成搜索觸發頻率如何控制?ios
問什麼要控制自動完成搜索的觸發頻率呢?你們都知道,自動完成觸發基本上都是鍵盤響應事件,在文本框中輸入一個文本就觸發一次,這個太頻繁了。好比我要輸入的搜索條件是abcdefg,輸入a觸發一次,接着輸入b再觸發一次,接着輸入c又觸發一次...,等到我把這幾個字母所有輸完就觸發了7次,假設請求邏輯沒有限制的話,這就會發生7次數據請求,而實際上只有第7次的結果纔是我想要的,其它6次數據請求徹底是浪費資源。在用戶需求上來講,用戶真正但願的是搜索框能自動識別到用戶的abcdefg這一串字符所有輸入完了,而後再觸發請求數據。對於一些變態的輸入,好比按住某一個建不放,還不知道要觸發多少次。angularjs
因此控制自動完成搜索的觸發頻率仍是頗有必要的。固然,搜索框能徹底智能的知道用戶所想而後去觸發,在目前來講仍是作不到的。axios
我這裏使用控制空閒時間的間隔的方式來限制自動完成搜索的觸發頻率。使用lodash庫中的debounce方法。segmentfault
問題2:數據還在請求中時再次觸發請求,上次的請求如何取消?數組
爲何要取消上次的請求你?舉個例了,我輸入了查詢條件"xxx",數據請求發送了,咱們暫且把它稱爲請求1。由於某些緣由(好比說網絡很差,這個條件的數據量大等等),請求1響應很慢,咱們在請求1還沒用拿到數據的時候又輸入查詢條件"yyy"發送了請求2,沒想到這個請求2數據響應特別快,一會兒就獲得了數據data,咱們準備把data展現出來,這時候請求1的數據回來了,data會被覆蓋掉,也就是說這時候咱們用"yyy"的條件查詢獲得了"xxx"條件的查詢結果。這個結果實際上是超出用戶指望的。因此在發送新的請求以前,若是上次的請求尚未結束,咱們就須要取消掉它。網絡
我這裏使用axios的CancelToken來取消請求。測試
下面列出主要代碼:
hello.vue:
<template> <div class="hello"> <!--使用mint-ui的搜索組件--> <mt-search v-model="searchKey" @input="search"></mt-search> <span>{{result}}</span> </div> </template> <script> import _ from 'lodash'; //引入lodash import axios from 'axios' //引入axios //請求canceltoken列表 let sources = []; export default { name: 'hello', data () { return { searchKey: '', //查詢條件 result: '' //查詢結果 } }, methods: { //使用_.debounce控制搜索的觸發頻率 //準備搜索 search: _.debounce( function () { let that = this; //刪除已經結束的請求 _.remove(sources, function (n) { return n.source === null; }); //取消還未結束的請求 sources.forEach(function (item) { if (item !== null && item.source !== null && item.status === 1) { item.status = 0; item.source.cancel('取消上一個') } }); //建立新的請求cancelToken,並設置狀態請求中 var sc = { source: axios.CancelToken.source(), status: 1 //狀態1:請求中,0:取消中 }; //這個對象加入數組中 sources.push(sc);
//開始搜索數據,yourhttp替換成你本身的請求路徑 axios.get('yourhttp', { cancelToken: sc.source.token }).then(function (res) { //請求成功 sc.source = null; //置空請求canceltoken //TODO這裏處理搜索結果 console.log(res.data); that.result = res.data; }).catch(function (thrown) { //請求失敗 sc.source = null; //置空請求canceltoken //下面的邏輯其實測試用 if (axios.isCancel(thrown)) { console.log('Request canceled', thrown.message); } else { //handle error } }); }, 500 //空閒時間間隔設置500ms ) } } </script> <style scoped> .mint-search { height: auto; } </style>
個人測試效果圖: