簡介
寫過一些前端互動網頁之後,多少會使用到一些事件被觸發之後的一些動作,例如:滑鼠移動、滾動等事件,但這些事件會在短時間內頻繁被觸發,如果在這一些事件上綁定了很多 DOM 事件很有可能會引起大量的效能消耗。
而解決這種過度頻繁觸發的問題常見的有兩種解法:Debounce 和 Throttle。
Debounce-去抖動
Debounce 處理連續觸發事件的時候,在某段時間內只有最後一次會被執行,而且如果事件持續被觸發,那時間就會重新計算。
Debounce 範例 - 連續 input 輸入
設計 Debounce 函式
所有事件觸發都會透過這個函式來處理,程式碼如下:
const debounce = (func, delay) => {
let timeId = null;
return (...args) => {
if(timeId) clearTimeout(timeId);
timeId = setTimeout(
() => { func.apply(this, args); },
delay
);
}
}
- debounce 會帶入兩個參數,一個是觸發事件後要執行的函式,一個是時間間隔多久觸發要執行的函式。
- clearTimeout() 是為了符合「事件持續被觸發,時間就會重新計算」,清除後設置一個新的 setTimeout 重新計時。
- debounce 最後回傳當中的參數是為了處理 func 傳入的參數。
HTML、其他事件監聽跟 callback 函式
const showLog = (e) => {
result.innerHTML = e.target.value;
document.getElementById('inputme')
.addEventListener('input', debounce(showLog, 500));
這裡就是監聽 inputme 的 input 事件,並且新增一些 HTML 元素。
<input id="inputme">
oninput: <span id="result"></span>
來看一下效果
可以看得出來,當我完成輸入後的 500ms 函式才會被執行。
Throttle-節流
Throttle 處理連續觸發事件的時候,只會在固定間隔時間才會執行,如果間隔中間有事件被觸發,都會被忽視。
Throttle 範例 - 連續點擊按鈕
設計 Throttle 函式
一樣所有事件都透過這個函式處理,程式碼如下:
const throttle = (func, delay) => {
let timeId = null;
return (...args) => {
if(!timeId){
timeId = setTimeout(
() => {
clearTimeout(timeId);
timeId = null;
func.apply(this, args);
}, delay
)
}
}
}
- throttle 一樣帶入兩個參數,一個函數、一個間隔
- return 部分一樣處理傳入函式的參數
- 如果 setTimeout 還存在,就不會執行下一個函式
HTML 跟其他函式
const showLog = (e) => {
console.log("Click!");
}
document.getElementById('clickme')
.addEventListener('click', throttle(showLog, 1000));
一樣新增 callback 跟監聽函式,並且新增一個 button
<button id="clickme">Click</button>
來看一下效果
這裡跟 debounce 不同的地方是無論我點擊多少次按鈕,都固定會間隔 1000ms 執行,而中間都會被忽略。
結論
- Debounce 只執行特定時間內被觸發的最後一次事件,而且如果事件不斷被觸發,時間就會重新計算(意味著事件不會被執行直到事件被停止觸發)
- Throttle 則是在固定間隔執行,在間隔中間被觸發的是件都會被忽略
目前任職於某國立科大計算機中心。專注於網頁前、後端技術(Laravel / ReactJS / VueJS / ASP.NET MVC),下班閒暇之餘就寫一些筆記紀錄所學,也試著寫出更有人性的資訊相關文章,也喜歡透過跑步釋出腦空間。