淺談js防抖和節流
在作業專案中我們肯定會遇到用戶某個操作,會導致頻繁觸發我們的函式,
其實這并不是我們想要的,而且從性能上講也是不可取的,有的可能還會導致bug,常見的有頁面的滾動潭訓動,按鈕的點擊·····那么能不能通過某個操作降低這些函式的觸發頻率?這就要用到防抖和節流了
防抖(debounce)
所謂防抖,就是指觸發事件后在 n 秒內函式只能執行一次,如果在 n 秒內又觸發了事件,則會重新計算函式執行時間,
節流(throttle)
所謂節流,就是指連續觸發事件但是在 n 秒中只執行一次函式,節流會稀釋函式的執行頻率,
下面通過監聽瀏覽器的滾動會頻繁觸發監聽事件來說一下如何實作這兩個函式
一個簡單的監聽滾動條事件的函式
let count = 0;
const showTop = () => {
let top = document.documentElement.scrollTop;
console.log(top, (count += 1));
};
window.onscroll = showTop;

點擊滾動條的向下按鈕我們會發現,監聽函式執行了很多次,然而實際上我們并不需要如此高頻的反饋,畢竟瀏覽器的性能是有限的,不應該浪費在這里,所以接著討論如何優化這種場景,
防抖(debounce)
基于上述場景,首先提出第一種思路:在第一次觸發事件時,不立即執行函式,而是給出一個期限值比如200ms,然后:
- 如果在200ms內沒有再次觸發滾動事件,那么就執行函式
- 如果在200ms內再次觸發滾動事件,那么當前的計時取消,重新開始計時
效果:如果短時間內大量觸發同一事件,只會執行一次函式,
實作:既然前面都提到了計時,那實作的關鍵就在于setTimeout這個函式,由于還需要一個變數來保存計時,考慮維護全域純凈,可以借助閉包來實作:
/**
*
* @param callback 需要防抖的函式
* @param time 延時的時間
*/
const debounce = (callback: () => void, time: number) => {
let timer: NodeJS.Timeout | null = null;
console.log('timer', timer);
return function() {
if (timer) {
// 進入該分支陳述句,說明當前正在一個計時程序中,并且又觸發了相同事件,所以要取消當前的計時,重新開始計時
clearTimeout(timer);
}
// 每次執行setTimeou() 都會回傳一個計時器的編號,用timer保存這個編號,用于清除計時器重新計時
timer = setTimeout(callback, time);
console.log('timer2', timer);
};
};
// 然后是舊代碼
let count = 0;
const showTop = () => {
let top = document.documentElement.scrollTop;
console.log(top, (count += 1));
};
window.onscroll = debounce(showTop,1000);
此時會發現,必須在停止滾動1秒以后,才會列印出滾動條位置,
到這里,已經把防抖實作了,現在給出定義:
對于短時間內連續觸發的事件(上面的滾動事件),防抖的含義就是讓某個時間期限(如上面的1000毫秒)內,事件處理函式只執行一次,
節流(throttle)
但是如果某個用戶閑著無聊一直不停的拖動滾動條,那么我們的事件是不是就一直不會被觸發了,這個也不是我們想要的效果,這個時候我們可能會想要哪怕用戶一直不停的觸發,事件也能在特定的時間間隔內執行一次,這樣是不是也能達到我們想要的效果,
原理大概就是:我們可以設計一種類似控制閥門一樣定期開放的函式,也就是讓函式執行一次后,在某個時間段內暫時失效,過了這段時間后再重新激活(類似于技能冷卻時間),
效果:如果短時間內大量觸發同一事件,那么在函式執行一次之后,該函式在指定的時間期限內不再作業,直至過了這段時間才重新生效,
實作:這里借助setTimeout來做一個簡單的實作,加上一個狀態位valid來表示當前函式是否處于作業狀態:
/**
*
* @param callback 需要防抖的函式
* @param time 延時的時間
*/
const throttle = (callback: () => void, time: number) => {
let valid = true;
return function() {
if (!valid) {
return false;
}
valid = false;
setTimeout(() => {
callback(), (valid = true);
}, time);
};
};
// 然后是舊代碼
let count = 0;
const showTop = () => {
let top = document.documentElement.scrollTop;
console.log(top, (count += 1));
};
window.onscroll = throttle(showTop,1000);
其他應用場景舉例
講完了這兩個技巧,下面介紹一下平時開發中常遇到的場景:
- 搜索框input事件,例如要支持輸入實時搜索可以使用節流方案(間隔一段時間就必須查詢相關內容),或者實作輸入間隔大于某個值(如500ms),就當做用戶輸入完成,然后開始搜索,具體使用哪種方案要看業務需求,
- 頁面resize事件,常見于需要做頁面適配的時候,需要根據最終呈現的頁面情況進行dom渲染(這種情形一般是使用防抖,因為只需要判斷最后一次的變化情況)
原文:https://segmentfault.com/a/1190000018428170
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/221452.html
標籤:其他
下一篇:哈希沖突詳解
