實作一個搜尋功能,當輸入框中的文字改變時,就去請求結果。
一開始是這樣寫的:
<input type="text" onkeydown={search} />
search = () => {
requestSearchResult();
}
但存在很大的問題,僅輸入"數學"兩個字就會使得keydown事件觸發7次,發7次請求。
通過上圖可以看出,搜尋内容稍有改變就會發新的請求,導緻請求過于頻繁。但這些請求中真正有用的其實是最後一個,最有一個請求發出時我們已經輸入完完整的搜尋詞,請求的結果也是我們真正想搜尋的。前面的請求都是沒有意義的,是對網絡資源的浪費。
為了讓請求不那麼頻繁,利用setTimeout做了一下處理:
<input type="text" onkeydown={search} />
search = () => {
setTimeout (() => {
requestSearchResult();
}, 500);
}
以上代碼就是當搜尋内容改變時,不是立即去請求,而是等500ms之後再去請求,這樣的話這500ms之内的改變都會被當成一個改變去發起請求,減少了一定的請求數。
可以看出請求數量确實減少了,但是還是存在無意義的請求。雖然将延遲的時間調大可以更有效的減少請求數,但是會導緻輸入結束後等待結果的時間變長,體驗很不好。
推薦的解決方案:debounce(防抖)。debounce在事件和函數執行之間加了一個控制層,來控制函數的執行次數。
var debounce = require('lodash.debounce');
<input type="text" onkeydown={search} />
// 一定要注意,debounce隻執行一次,執行後的函數作為事件處理函數
GOOD:
search = debounce(requestSearchResult(), 500);
BAD:
search = () => {
debounce(requestSearchResult(), 500)();
}
使用debounce之後,它隻會在最後一次改變發生後才會去執行函數,隻發送一次請求。
Lodash和underscore都有debounce的API,可以直接拿來用,以下是loadash.debounce使用方法:
npm i --save lodash.debounce
var debounce = require('lodash.debounce');
以下是網上看到的用es6實作的debounce代碼:
export default function debounce(func, wait, immediate) {
let timeout
return function(...args) {
clearTimeout(timeout)
timeout = setTimeout(() => {
timeout = null
if (!immediate) func.apply(this, args)
}, wait)
if (immediate && !timeout) func.apply(this, [...args])
}
}
上面的代碼也是可以有效減少請求數量,但是并不能将請求數量減到1,還是會有一些無謂的請求。建議用Lodash和underscore。