js 防抖實戰講解

andrewjm發表於2019-05-08

為什麼要用防抖技術

在前端開發中會遇到一些頻繁的事件觸發,比如:
window 的 resize、scroll
mousedown、mousemove
keyup、keydown
……
為此,我們舉個示例程式碼來了解事件如何頻繁的觸發:

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>頻繁觸發例子</title>
</head>
<body>
	 <input id="search" type="text" placeholder="請輸入要查詢的內容">
	 <script>
	  	 var search = document.getElementById('search');
			function getUserAction() {
			   console.log('執行查詢操作',+new Date());
			};

	    search.addEventListener('keyup', getUserAction)
	 </script>
</body>
</html>
複製程式碼

效果圖如下:

mingzi1.gif

一輸入內容,就會一直執行getUserAction 函式!
因為這個例子很簡單,所以瀏覽器完全反應的過來,可是如果是複雜的回撥函式或是 ajax 請求呢?假設 1 秒觸發了 10 次,每個回撥就必須在 1000 / 10 = 10ms 內完成,否則就會有卡頓出現。
為了解決這個問題,我們可以使用防抖

防抖原理

在事件被觸發n秒後,再去執行回撥函式。如果n秒內該事件被重新觸發,則重新計時。結果就是將頻繁觸發的事件合併為一次,且在最後執行。

第一個例子

程式碼如下:

var search = document.getElementById('search');
function getUserAction(e) {
    console.log(e);
    console.log('執行查詢操作',+new Date());
  };

function debounce(func, wait) {
    var timeout;
    return args => {
      if (timeout) clearTimeout(timeout) 
      timeout = setTimeout(func, wait);
    }
}

const debounceAjax = debounce(getUserAction, 1000)

subBtn.addEventListener('keyup', debounceAjax)
複製程式碼

輸入後1秒執行方法,一直輸入以輸入完為準,1秒執行方法,點選看看使用效果:

mingzi1.gif

第二個例子

立刻執行
有這樣一個需求,滑鼠移動事件,移動到內容上,立即執行函式.

根據這段表述,我們可以寫的程式碼:

function debounce(func, wait, immediate) {
    var timeout;
    return args => {
        let context = this;
        if (timeout) clearTimeout(timeout) 
        if (immediate) {
            // 如果已經執行過,不在執行
            let callNow = !timeout;
            timeout = setTimeout(function() {
                timeout = null;
            },wait) 
          if (callNow) func.call(context, args)
        } else {
            timeout = setTimeout(() => {
                func.call(context, args)
            },wait)
        }
    }
}
複製程式碼

debounce(getUserAction, 1000, true)
滑鼠移動到內容上立即執行方法,重複移動以最後一次移動為準1秒後執行方法,看看使用效果:

mingzi1.gif

完善功能增加取消防抖功能

function debounce(func, wait, immediate) {
    var timeout,result;
    var debounced =  function() {
      var context = this;
      var args = arguments;

      if (timeout) clearTimeout(timeout);
      if (immediate) {
        // 如果已經執行過,不再執行
        var callNow = !timeout;
        timeout = setTimeout(function() {
          timeout = null;
        }, wait)
        if (callNow) result = func.apply(context, args)
      } else {
        timeout = setTimeout(function() {
          func.apply(context, args)
        }, wait);
      }

      return result;
    }

    debounced.cancel = function() {
    	  clearTimeout(timeout);
    	  timeout = null;
    }

    return debounced;
  }
複製程式碼

執行程式碼:

var setUseAction = debounce(getUserAction, 1000, true);
  container.onmousemove = function() {
    var res = setUseAction();
    console.log(res)
  }

  document.getElementById("button").addEventListener('click', function() {
    setUseAction.cancel();
  })
複製程式碼

設定時間間隔較大10秒,當點選取消防抖後立即執行了,看看使用效果:

mingzi1.gif

參考地址:
   github.com/mqyqingfeng…

相關文章