1. 程式人生 > 實用技巧 >js節流與防抖

js節流與防抖

節流和防抖這是兩個類似又有些不同的優化方案

日常開發過程中,滾動事件做複雜計算頻繁呼叫回撥函式很可能會造成頁面的卡頓,這時候我們更希望把多次計算合併成一次,只操作一個精確點,JS把這種方式稱為debounce(防抖)和throttle(節流)

節流:高頻事件觸發,但在n秒內只會執行一次,所以節流會稀釋函式的執行頻率。即:在指定時間之內,讓函式只觸發一次。
防抖:觸發高頻事件後n秒內函式只會執行一次,如果n秒內高頻事件再次被觸發,則重新計算時間。即:對於一定時間段的連續的函式呼叫,只讓其執行一次。

節流(debounce)

throttle在英語裡的意思是節流閥,顧名思義,設定一個閥值(制定一個時間),在這個閥值或者時間之內,函式只會執行一次。

實現函式節流我們主要有兩種方法:時間戳和定時器

舉個例子,我們執行頁面滾動的時候,比如在react裡面,可能每次滾動都會觸發一次render,這樣嚴重影響效能,甚至會造成瀏覽器卡死。如果我們設定一個300ms的時間閥,那麼在這段時間內,滾動時候只會觸發一次render.

同樣的,當我們拖拽某個元素的時候,會每次判斷mousemove時跟位置相關的資訊,每次都會執行相關的計算和判斷,這種情況就和滾動時候一樣,如果設定一個時間閥,那麼就可以避免由於大量執行事件計算而造成的效能下降。

  • 實現方式:每次觸發事件時設定一個延遲呼叫方法,並且取消之前的延時呼叫方法
  • 缺點:如果事件在規定的時間間隔內被不斷的觸發,則呼叫方法會被不斷的延遲
function throttle(fn,times = 300){
    let bool = true
    return function(){
        if(!bool){
            return false
        }
        bool = false
        setTimeout(()=>{
            bool = true
            fn.apply(this,arguments)
        },times)
    }
    
}

或者

onresize問題,頁面滿屏佈局,模組很多dom結構也相對複雜。所以在視窗頻繁快速變化大小的時候頁面反應異常卡頓。

//問題解決的原理就是事件節流

window.onresize = () => {
      console.log('resize')
}

解決辦法如下:

 let timer = null
          window.onresize = () => {
                console.log(timer)
                if (!timer) {
                    timer = setTimeout(() => {
                        callBack()
                        timer = null
                    }, 1000)
                }
            }

            function callBack() {
                console.log('resize')
            }

封裝起來:

//封裝前我們先思考一下,首先既然是封裝那麼事件不一定都是onersize、間隔時間得有使用者設定、callBack得是使用者寫。

**其實我們只關心callBack,和執行間隔時間,恰好事件都有回撥

            function callBack() {
                console.log('resize')
            }

            function throttle(callBack, time) {
                let timer = null
                //timer狀態要常駐記憶體,這裡做了一個閉包
                return function() {
                    if (!timer) {
                        timer = setTimeout(() => {
                            callBack()
                            timer = null
                        }, time)
                    }
                }
            }

            window.addEventListener('resize', throttle(callBack, 1000), false)

防抖(throttle)

防抖實現起來的思路是,用閉包儲存執行的函式,多次執行的時候把上一個執行的函式清除掉,然後再次建立一個新的函式。這樣在間隔時間內還有事件觸發的話,不會執行之前的函式,這麼一來,函式真正的執行就是最後一次事件觸發。

實現方式:每次觸發事件時,如果當前有等待執行的延時函式,則直接return

function debounce(fn,times){
    let timeout = null
    return function(){
        clearTimeout(timeout)
        timeout = setTimeout(()=>{
            fn.apply(this,arguments)
        },times)
    }
}

總結:

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

函式節流:使得一定時間內只觸發一次函式。原理是通過判斷是否有延遲呼叫函式未執行。

區別: 函式節流不管事件觸發有多頻繁,都會保證在規定時間內一定會執行一次真正的事件處理函式,而函式防抖只是在最後一次事件後才觸發一次函式。 比如在頁面的無限載入場景下,我們需要使用者在滾動頁面時,每隔一段時間發一次 Ajax 請求,而不是在使用者停下滾動頁面操作時才去請求資料。這樣的場景,就適合用節流技術來實現。

兩者都是在密集呼叫的過程中靈活使用setTimeout函式來對頻繁觸發的事件進行控制和優化。