使用window.addEventListener()進(jìn)行窗口的resize、scroll、輸入框?qū)崟r(shí)監(jiān)控等操作時(shí),如果時(shí)間處理函數(shù)調(diào)用的頻率無限制,會(huì)加重瀏覽器的負(fù)擔(dān),容易引起瀏覽器卡死,用戶體驗(yàn)非常的糟糕。
此時(shí)我們可以采用debounce(防抖)和throttle(節(jié)流)的方式減少調(diào)用頻率,同時(shí)又不影響實(shí)際效果。
函數(shù)防抖
函數(shù)防抖(debounce):當(dāng)持續(xù)觸發(fā)scroll事件,不會(huì)執(zhí)行handle函數(shù),當(dāng)停止scroll事件的一段時(shí)間(1000毫秒)之后事件處理函數(shù)才會(huì)執(zhí)行一次。 如果設(shè)定的時(shí)間到來之前,又一次觸發(fā)了事件,就重新開始延時(shí)。
防抖debounce代碼:
// 防抖
debounce(fn,delay){
let timer = null //借助閉包
return function() {
if(timer){
clearTimeout(timer)
}
timer = setTimeout(fn,delay);
}
}
// 處理函數(shù)
function handle() {
console.log(Math.random());
}
// 滾動(dòng)事件
window.addEventListener('scroll', debounce(handle, 1000));
當(dāng)持續(xù)觸發(fā)scroll事件時(shí),事件處理函數(shù)handle只在停止?jié)L動(dòng)1000毫秒之后才會(huì)調(diào)用一次,也就是說在持續(xù)觸發(fā)scroll事件的過程中,事件處理函數(shù)handle不會(huì)一直執(zhí)行。
函數(shù)節(jié)流
函數(shù)節(jié)流(throttle):當(dāng)持續(xù)觸發(fā)事件時(shí),保證一定時(shí)間段內(nèi)只調(diào)用一次事件處理函數(shù)。按照一定規(guī)律在某個(gè)時(shí)間間隔內(nèi)執(zhí)行。如下圖,持續(xù)觸發(fā)scroll事件時(shí),并不立即執(zhí)行handle函數(shù),每隔1000毫秒才會(huì)執(zhí)行一次handle函數(shù)。
throttle(fn,delay){
let valid = true
return function() {
if(!valid){
//休息時(shí)間 暫不接客
return false
}
// 工作時(shí)間,執(zhí)行函數(shù)并且在間隔期內(nèi)把狀態(tài)位設(shè)為無效
valid = false
setTimeout(() => {
fn()
valid = true;
}, delay)
}
},
// 處理函數(shù)
function handle() {
console.log(Math.random());
}
// 滾動(dòng)事件
window.addEventListener('scroll', debounce(handle, 1000));
如果一直拖著滾動(dòng)條進(jìn)行滾動(dòng),那么會(huì)以1s的時(shí)間間隔,持續(xù)執(zhí)行handle事件。
其他應(yīng)用場景舉例
1) 在頁面的無限加載場景下,我們需要用戶在滾動(dòng)頁面時(shí),每隔一段時(shí)間發(fā)一次 Ajax 請(qǐng)求,而不是在用戶停下滾動(dòng)頁面操作時(shí)才去請(qǐng)求數(shù)據(jù)。這樣的場景,就適合用節(jié)流技術(shù)來實(shí)現(xiàn)。
2) 搜索框input事件,例如要支持輸入實(shí)時(shí)搜索可以使用節(jié)流方案(間隔一段時(shí)間就必須查詢相關(guān)內(nèi)容),或者實(shí)現(xiàn)輸入間隔大于某個(gè)值(如500ms),就當(dāng)做用戶輸入完成,然后開始搜索可以使用防抖,具體使用哪種方案要看業(yè)務(wù)需求。
3)頁面resize事件,常見于需要做頁面適配的時(shí)候。需要根據(jù)最終呈現(xiàn)的頁面情況進(jìn)行dom渲染(這種情形一般是使用防抖,因?yàn)橹恍枰袛嘧詈笠淮蔚淖兓闆r)
4) 避免按鈕短時(shí)間內(nèi)重復(fù)點(diǎn)擊頻繁的觸發(fā)事件,可采用防抖,只執(zhí)行最后一次操作。
總結(jié)
函數(shù)防抖:將幾次操作合并為一此操作進(jìn)行。原理是維護(hù)一個(gè)計(jì)時(shí)器,規(guī)定在delay時(shí)間后觸發(fā)函數(shù),但是在delay時(shí)間內(nèi)再次觸發(fā)的話,就會(huì)取消之前的計(jì)時(shí)器而重新設(shè)置。這樣一來,只有最后一次操作能被觸發(fā)。
函數(shù)節(jié)流:使得一定時(shí)間內(nèi)只觸發(fā)一次函數(shù)。原理是通過判斷是否到達(dá)一定時(shí)間來觸發(fā)函數(shù)。
區(qū)別: 函數(shù)節(jié)流不管事件觸發(fā)有多頻繁,都會(huì)保證在規(guī)定時(shí)間內(nèi)一定會(huì)執(zhí)行一次真正的事件處理函數(shù),而函數(shù)防抖只是在最后一次事件后才觸發(fā)一次函數(shù)。