前端概念

前端效能優化 - Debounce 和 Throttle

5分鐘

2023-01-20 01:27:00

簡介

寫過一些前端互動網頁之後,多少會使用到一些事件被觸發之後的一些動作,例如:滑鼠移動、滾動等事件,但這些事件會在短時間內頻繁被觸發,如果在這一些事件上綁定了很多 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
        );
    }
}
  1. debounce 會帶入兩個參數,一個是觸發事件後要執行的函式,一個是時間間隔多久觸發要執行的函式。
  2. clearTimeout() 是為了符合「事件持續被觸發,時間就會重新計算」,清除後設置一個新的 setTimeout 重新計時。
  3. 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
            )
        }
    }
}
  1. throttle 一樣帶入兩個參數,一個函數、一個間隔
  2. return 部分一樣處理傳入函式的參數
  3. 如果 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 執行,而中間都會被忽略。

結論

  1. Debounce 只執行特定時間內被觸發的最後一次事件,而且如果事件不斷被觸發,時間就會重新計算(意味著事件不會被執行直到事件被停止觸發)
  2. Throttle 則是在固定間隔執行,在間隔中間被觸發的是件都會被忽略

目前任職於某國立科大計算機中心。專注於網頁前、後端技術(Laravel / ReactJS / VueJS / ASP.NET MVC),下班閒暇之餘就寫一些筆記紀錄所學,也試著寫出更有人性的資訊相關文章,也喜歡透過跑步釋出腦空間。