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函式來對頻繁觸發的事件進行控制和優化。