前端項目開發過程中,對一個dom元素動作綁定了事件,但觸發dom函數的動作過於頻繁從而影響頁面性能甚至出現bug的情況,比如: 頁面滾動scroll事件、瀏覽器視窗resize事件、輸入框搜索input事件等等,這些事件如果在一段時間內不加限制頻繁觸發必定會導致頁面性能變差,尤其是綁定的事件內包含觸 ...
前端項目開發過程中,對一個dom元素動作綁定了事件,但觸發dom函數的動作過於頻繁從而影響頁面性能甚至出現bug的情況,比如:
頁面滾動scroll事件、瀏覽器視窗resize事件、輸入框搜索input事件等等,這些事件如果在一段時間內不加限制頻繁觸發必定會導致頁面性能變差,尤其是綁定的事件內包含觸發頁面重繪重排、ajax請求這類操作時,甚至可能出現卡頓、假死、數值錯誤之類的bug。。。
所以為了防止綁定事件多次觸發,就需要考慮使用防抖和節流的方案了
防抖:事件在指定時間間隔後觸發,在時間間隔內如果重覆觸發事件就重新計時,多用於取多次操作的最後一次操作為有效操作的場景如:搜索框輸入搜索、點擊切換狀態按鈕、表單數據提交
節流:事件在指定事件間隔內只觸發一次,時間間隔內重覆觸發則只有第一次生效,超出時間間隔直接執行,多用於多次操作會取得相同結果從而避免多次觸發的情況如:滾動載入、新增列表項、拖拽元素
防抖和節流在代碼中如何實現:
防抖
1 /** 2 * @param {*} fn 要進行防抖的函數 3 * @param {*} delay 延遲幾秒執行 4 * @param {*} immediate 是否立即執行一次防抖函數(fn) 5 */ 6 function debounce(fn, delay, immediate) { 7 let timeout = null 8 return function(...args) { 9 // 清除已存在定時任務 10 if(timeout) { 11 clearTimeout(timeout) 12 timeout = null 13 } else { 14 if(immediate) fn.apply(this, args) 15 } 16 // 重新設置定時任務 17 timeout = setTimeout(() => { 18 fn.apply(this, ...args) 19 }, delay) 20 } 21 }
節流
1 /** 2 * 立即執行版本 3 * @param {*} fn 要執行節流的函數 4 * @param {*} wait 節流函數執行周期 5 */ 6 function throttle(fn, wait) { 7 let pre = Date.now() 8 return function(...args) { 9 let now = Date.now() 10 if(now - pre > wait) { 11 fn.apply(this, args) 12 pre = now 13 } 14 } 15 } 16 17 /** 18 * 延遲指定時間執行版本 19 * @param {*} fn 要執行節流的函數 20 * @param {*} wait 節流函數執行周期 21 */ 22 function throttle(fn, wait) { 23 let timeout = null 24 return function(...args) { 25 if(!timeout) { 26 setTimeout(() => { 27 fn.apply(this, args) 28 timeout = null 29 }, wait) 30 } 31 } 32 }
最後我們來看一下使用了防抖和節流後的效果:
防抖:
這裡通過在控制台輸出input框內容模擬實際應用場景中用戶手動輸入搜索,可以看到沒有使用防抖函數,用戶每次輸入都會觸發搜索操作,非常損耗性能
使用防抖後(這裡的防抖延遲我設定的是1s),可以看到只有當用戶輸入間隔超過1s後才會真正執行搜索邏輯,大大節省了性能
節流:
通過頁面滾動條滾動到指定高度時觸發控制台輸出不同內容模擬滾動載入,可以看到在不使用節流的情況下,滾動條滾動到指定高度後繼續滾動還會觸發載入函數
而使用了節流後,即使多次滾動也只會觸發一次載入函數,大大節省了性能