以前寫了一篇長列表實現的分享 -- 「前端長列表」開源庫解析及最佳實踐html
而後面試被問到的最多的問題就是:有沒有作節流前端
當時的回答是沒有,可是又說不出一個系統的回答web
本文就來講說爲何長列表不須要作節流,以及什麼狀況下滾動須要作節流面試
詳見HTML規範api
這裏只挑與本文相關的講併發
在事件循環中定義了不少任務源,好比鼠標鍵盤等輸入操做的用戶交互任務源app
一次點擊操做,其實包含多個輸入操做(mousedown,mouseup,click),都添加到相同的任務源隊列中,進而產生多輪的事件循環,其表現就是執行相關元素的事件回調(宏任務)webapp
宏任務執行後,後面就是微任務隊列和更新渲染階段oop
每輪事件循環多是很是快的,每秒執行事件循環的次數可能大於60次post
受硬件刷新率影響,咱們只要保證 fps 達到最大硬件刷新率(好比60)便可,所以不須要每輪事件循環都更新渲染
對於輸入事件,其執行時機爲每輪事件循環的任務執行階段,這個事件是不受刷新率影響的,每秒的執行次數可能多於60次
爲何談這個呢,由於這個與滾動事件(scroll)回調的執行時機不一致
按照 HTML 規範,滾動事件回調在 UI Render 階段的某個步驟中進行,而不是單獨的一個任務源
也就是說,滾動事件回調受渲染時機影響,僅執行更新渲染時才執行該回調。
換句話說,該事件自帶節流。
舉個例子驗證下輸入事件和更新渲染的執行時機
document.addEventListener("mousemove",function(){
let start = performance.now()
console.log("mousemove:",start)
requestAnimationFrame(function(t){console.log("ui render:",start,t)})
})
// 結果就是可能輸出幾輪 mousemove 而後執行一次 ui render -- 清空 rAF 回調隊列(輸出屢次 ui render)
/* mousemove: 4091.025000088848 ui render: 4091.025000088848 4077.594 mousemove: 4098.845000029542 ui render: 4098.845000029542 4094.278 mousemove: 4110.160000040196 mousemove: 4115.5349999899045 ui render: 4110.160000040196 4110.962 ui render: 4115.5349999899045 4110.962 mousemove: 4123.810000019148 mousemove: 4130.160000058822 ui render: 4123.810000019148 4127.719 ui render: 4130.160000058822 4127.719 */
複製代碼
說明更新渲染有必定的間隔,至少是 1/60 的間隔,而輸入任務沒有此限制
因此,如下代碼是沒有效果的,由於該回調已經自帶節流了
document.addEventListener("scroll",function(e){
requestAnimationFrame(function(t){
//執行scroll具體邏輯
})
})
複製代碼
利用節流能夠減小回調的執行次數,使得固定時間週期內只執行一次
剛纔提到,滾動事件自帶節流,節流的時間週期是與渲染時機相關
判斷是否須要額外的節流的關鍵是:當前的節流規則,是否大部分回調的執行都能讓用戶受益
若是是動畫效果,實時繪製的界面等,則不須要額外的節流了。
以長列表爲例,每次執行滾動回調,會計算新的渲染列表項及滾動偏移位置。若是應用更大時間週期的節流,會出現某一幀出現滾動但界面沒有更新的狀況,讓用戶感受產生卡頓。
而其餘比較複雜的業務邏輯,不能在短期內獲得反饋的,則須要額外進行節流
以滾動懶加載圖片爲例
因爲每秒的滾動回調的執行次數可能達到60次,而每次執行都須要去獲取當前處於視區的佔位圖併發起圖片請求
而這大部分回調的執行,用戶是不能受益的,因此咱們能夠提升節流的時間週期,好比 500ms 這樣
滾動事件已自帶節流,只有一些特定的業務邏輯才須要額外進行更高時間週期的節流