背景
當我們進行窗口resize、scroll
挨厚、input框內(nèi)容校驗等操作時,如果事件函數(shù)調(diào)用頻率不加控制甥材。會加重瀏覽器的負擔绑改,導致用戶體驗度差谢床。此時我們可以在不影響功能效果的前提下使用函數(shù)防抖和函數(shù)節(jié)流的方式來減少調(diào)用頻率。
防抖和節(jié)流
防抖
debounce
厘线,當事件觸發(fā)事件時识腿,一定時間段t
內(nèi)沒有再次觸發(fā)事件,事件處理函數(shù)才會執(zhí)行皆的,如果在時間段t
內(nèi)覆履,又觸發(fā)了一次函數(shù),就重新開始延時费薄,即再過t
時間后執(zhí)行硝全。
在我們的開發(fā)過程中,比如resize楞抡、scroll伟众、mousemove、mouseover等召廷,會頻發(fā)的觸發(fā)凳厢,如果不做限制的話,有可能1s內(nèi)執(zhí)行了幾十次竞慢、上百次先紫。如果在這些函數(shù)內(nèi)執(zhí)行了其他函數(shù),尤其是執(zhí)行了操作DOM的函數(shù)筹煮,那不僅僅會造成計算機資源的浪費遮精,還會降低程序運行速度,甚至會照成瀏覽器卡死败潦、崩潰本冲。
防抖的關(guān)鍵在于,在一個動作發(fā)生 一定時間之后劫扒,才執(zhí)行特定的事件檬洞。
let debounce = function(fn, delay = 500) {
if (typeof fn !== "function") {
throw new TypeError("Expected a function");
}
let timer = null;
return (...arg) => {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(function() {
fn.call(arg);
}, delay);
};
};
過程分析:
- 第一次觸發(fā)事件,不立即執(zhí)行
-
delay
時間內(nèi)沟饥,再次觸發(fā)事件添怔,清除定時器,重新開始計時 -
delay
時間后贤旷,執(zhí)行函數(shù)
通常情況下函數(shù)在觸發(fā)事件后一直等待广料,我們可以在第一次時間出觸發(fā)后立即執(zhí)行,但仍保持delay
時間內(nèi)多次觸發(fā)只執(zhí)行一次遮晚。lodash.debounce
通過leading
和trailing
來控制函數(shù)在delay前執(zhí)行性昭,還是delay后執(zhí)行。在underscore.js
通過immdiate
通知執(zhí)行的時機县遣。
適用場景:
- resize時間
- 觸發(fā)ajax請求
節(jié)流
throttle
糜颠,當持續(xù)觸發(fā)事件時,保證一定時間段t
內(nèi)只調(diào)用一次事件處理函數(shù)萧求。
側(cè)重于一段時間內(nèi)其兴,執(zhí)行一次。
let throttle = function(fn, delay = 400) {
if (typeof fn !== "function") {
throw new TypeError("Expected a function");
}
let flag = true;
return function(...args) {
if (!flag) return;
flag = false;
setTimeout(() => {
fn(...args);
flag = true;
}, delay);
};
};
過程分析:
- 第一次觸發(fā)事件夸政,元旬,開關(guān)項置為false
-
delay
時間段內(nèi),再次觸發(fā),不新計時 -
delay
時間后執(zhí)行函數(shù)匀归,并設(shè)置開關(guān)為true - 再次觸發(fā)事件,開關(guān)項置為false
- 距離上次觸發(fā)
delay
時間內(nèi)坑资,不執(zhí)行函數(shù) - 距離上次觸發(fā)
delay
時間后,執(zhí)行函數(shù)穆端,并設(shè)置開關(guān)true
適用場景:
- scroll事件
- 重復點擊事件
區(qū)別
防抖:時間段內(nèi)多次觸發(fā)袱贮,只執(zhí)行最后一次
節(jié)流:每隔固定時間段后保證穩(wěn)定執(zhí)行一次
一句話:防抖是將多次執(zhí)行變?yōu)橐淮螆?zhí)行,節(jié)流是將多次執(zhí)行變?yōu)槊扛粢欢螘r間執(zhí)行
requestAnimationFrame
可以認為它是一個_.throttle(dosomething, 16)
体啰。但保真度更高攒巍,因為它是旨在提高準確性的瀏覽器本機API。
requestAnimationFrame接受一個動畫執(zhí)行函數(shù)作為參數(shù)荒勇,這個函數(shù)的作用是僅執(zhí)行一幀動畫的渲染柒莉,并根據(jù)條件判斷是否結(jié)束,如果動畫沒有結(jié)束沽翔,則繼續(xù)調(diào)用requestAnimationFrame并將自身作為參數(shù)傳入
優(yōu)點:
- 以60FPS(每幀16ms)為目標兢孝,瀏覽器內(nèi)部會選擇渲染的最佳時機
- API簡單
缺點:
- 需要手動啟動和取消
- node.js不支持
適用場景:
- 函數(shù)是“繪畫”
- 直接對屬性進行動畫處理
參考:
https://css-tricks.com/debouncing-throttling-explained-examples/