前端效能最佳化|什麼是防抖和節流?

renke發表於2021-09-09

防抖與節流、節流與防抖,是不是經常讓你不知道誰是誰。

乘電梯,如果有人進入電梯(觸發事件),電梯門會等待 5 秒再關閉,在等待期間如果又有人按電梯進入(在 5 秒內再次觸發該事件),電梯門又要重新等待 5 秒,直到最後一個人進入電梯,電梯門在等待 5 秒後關閉。

這就是我們生活中遇見的防抖,那節流是什麼呢?

打過遊戲的朋友都知道,遊戲當中技能的使用是有冷卻時間的,就算你不停地按技能,也只能在規定時間內觸發一次,這就是節流。防抖和節流是前端最佳化經常遇見的知識點,快拿好小板凳和我一起開啟接下來的學習吧!

函式防抖(debounce)

1、原理

當持續觸發事件時,在規定的時間內該事件沒有被再次呼叫,事件處理函式就會執行一次,如果在規定時間內事件再次被呼叫,就重新開始計時。

2、使用場景

按鈕提交場景:防止多次提交按鈕,只執行最後提交的一次。

搜尋框的聯想詞:只傳送最後一次輸入結果的請求。

進行視窗的 resize、scroll 事件時: 只計算最後一次結果進行執行。

輸入框內容的校驗:只進行最後一次輸入的內容校驗。

function debounce(func, wait, immediate) {
  let timer;
  let debounced = function () {
    let _this = this;
    let args = arguments;
    clearTimeout(timer);
    if (immediate) {
      let executeNow = !timer;
      timer = setTimeout(function () {
        timer = null;
      }, wait);
      if (executeNow) {
        func.apply(_this, args);
      }
    } else {
      timer = setTimeout(function () {
        func.apply(_this, args)
      }, wait);
    }
    debounced.cancel = function () {
      clearTimeout(timer);
      timer = null;
    }
  }
  return debounced;
}

3、實現

用 immediate 引數控制函式是否立即執行。如果函式是立即執行的,就立即呼叫;如果函式是延遲執行的,就快取上下文和引數,放到延遲函式中去執行。一旦開始一個定時器,只要定時器還在,每次觸發函式都會重新計時。一旦事件不觸發了,定時器時間到,執行一次事件函式,定時器重置為 null,就可以再次點選了。

函式節流(throttle)

1、原理

當持續觸發事件時,保證在規定的時間內呼叫一次事件處理函式。如果這個單位時間內觸發多次函式,只有一次有效。

2、使用場景

DOM 元素的拖拽功能實現: 一個時間週期去獲取一次位置並計算,防止超高頻次觸發位置變動。
進行視窗的 resize、scorll 事件時: 只計算最後一次結果進行執行。

3、實現

**時間戳方式:**當觸發事件的時候,我們取出當前的時間戳,然後減去之前的時間戳,如果大於設定的時間週期,就執行函式,然後更新時間戳為當前的時間戳;如果小於,就不執行。該方式第一次事件立即執行,最後一次事件不執行。

function throttle(func,wait){
  let _this,args;
  let preTime = 0;
  return function(){
    _this = this;
    args = arguments;
    let now = new Date().valueOf();
    if(now-preTime > wait){
      func.apply(_this,args);
      preTime = now;
    }
  }
}

**定時器方式:**當觸發事件的時候,我們設定一個定時器,再觸發事件的時候,如果定時器存在,就不執行,直到定時器執行完,然後執行函式,清空定時器,這樣就可以設定下個定時器。該方式第一次事件不立即執行,最後一次事件執行。

function throttle(func,wait){
  let _this, args, timer;
  return function(){
    _this = this;
    args = arguments;
    if(!timer){
      timer = setTimeout(() => {
        timer = null;
        func.apply(_this,args);
      },wait);
    }
  }
}

**時間戳 + 定時器方式: **用函式引數實現配置項(leading:boolean、trailing:boolean),控制事件第一次是否立即觸發和事件最後一次是否觸發。

function throttle(func,wait,options){
  let _this, args, timer;
  let preTime = 0;
  if(!options) options = {};
  return function(){
    _this = this;
    args = arguments;
    let now = new Date().valueOf();
    if(options.leading === false && !preTime)  {
      preTime = now;
    }
    if((now - preTime) > wait){
      if(timer){
        clearTimeout(timer);
        timer = null;
      }
      func.apply(_this,args);
      preTime = now;
    } else if(!timer && options.trailing !== false){
      timer = setTimeout(() => {
        preTime = new Date().valueOf;
        timer = null;
        func.apply(_this,args);
      },wait);
    }
  }
}

總結

函式防抖:

將幾次操作合併為一次操作進行。原理是維護一個計時器,規定在 delay 時間後觸發函式,但是在 delay 時間內再次觸發的話,就會取消之前的計時器而重新設定。這樣一來,只有最後一次操作能被觸發。

函式節流:

使得一定時間內只觸發一次函式。原理是透過判斷是否到達一定時間來觸發函式。

兩者的區別:

函式節流不管事件觸發有多頻繁,都會保證在規定時間內一定會執行一次事件處理函式,而函式防抖只是在最後一次事件後才觸發一次函式。

作者

林茂

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/430/viewspace-2806993/,如需轉載,請註明出處,否則將追究法律責任。

相關文章