溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

JavaScript防抖與節(jié)流怎么使用

發(fā)布時間:2022-05-26 09:07:07 來源:億速云 閱讀:406 作者:zzz 欄目:開發(fā)技術(shù)

這篇文章主要講解了“JavaScript防抖與節(jié)流怎么使用”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“JavaScript防抖與節(jié)流怎么使用”吧!

    前言:

    防抖(Debounce) 和 節(jié)流(Throttle) 技術(shù)用于限制函數(shù)執(zhí)行的次數(shù)。通常,一個函數(shù)將被執(zhí)行多少次或何時執(zhí)行由開發(fā)人員決定。但在某些情況下,開發(fā)人員會將這種能力賦予用戶,由用戶決定執(zhí)行該功能的時間和次數(shù)。

    例如,添加到click、scrollresize等事件上的函數(shù),允許用戶決定何時執(zhí)行它們以及執(zhí)行多少次。有時,用戶可能會比所需更頻繁地執(zhí)行這些操作。這可能不利于網(wǎng)站的性能,特別是如果附加到這些事件的函數(shù)執(zhí)行一些繁重的計算。在這種情況下,用戶可以控制函數(shù)的執(zhí)行,開發(fā)人員必須設(shè)計一些技術(shù)來限制用戶可以執(zhí)行函數(shù)的次數(shù)。

    舉個例子,假設(shè)我們?yōu)闈L動事件scroll添加了一個函數(shù),該函數(shù)中會執(zhí)行修改DOM元素的操作。我們知道,修改DOM元素大小開銷很大,會引起瀏覽器的回流(Reflow)和重排(Repaint),以及重新渲染整個或部分頁面。如果用戶頻繁滾動,導(dǎo)致該函數(shù)頻繁被調(diào)用,可能會影響網(wǎng)頁性能或?qū)е马撁婵D等。
    此外,有些事件回調(diào)函數(shù)中包含ajax等異步操作的時候,多次觸發(fā)會導(dǎo)致返回的內(nèi)容結(jié)果順序不一致,而導(dǎo)致得到的結(jié)果非最后一次觸發(fā)事件對應(yīng)的結(jié)果

    所以,為了優(yōu)化網(wǎng)頁的性能,控制函數(shù)被調(diào)用的頻率是很有必要的,防抖(Debounce) 和 節(jié)流(Throttle) 是通過控制函數(shù)被調(diào)用的頻率來優(yōu)化腳本性能的兩種方法

    一、防抖(Debounce)

    防抖:無論用戶觸發(fā)多少次事件,對應(yīng)的回調(diào)函數(shù)只會在事件停止觸發(fā)指定事件后執(zhí)行。(即:回調(diào)函數(shù)在事件停止觸發(fā)指定時間后被調(diào)用)

    JavaScript防抖與節(jié)流怎么使用

    例如,假設(shè)用戶在 100 毫秒內(nèi)點擊了 5 次按鈕。防抖技術(shù)不會讓這些點擊中的任何一個執(zhí)行對應(yīng)的回調(diào)函數(shù)。一旦用戶停止點擊,如果去抖時間為 100 毫秒,則回調(diào)函數(shù)將在 100 毫秒后執(zhí)行。因此,肉眼看來,防抖就像將多個事件組合成一個事件一樣。

    1.1 防抖函數(shù)的實現(xiàn)

    (1)版本1 —— 停止觸發(fā)事件n毫秒后執(zhí)行回調(diào)函數(shù)

    觸發(fā)事件后函數(shù)不會立即執(zhí)行,而是在停止事件觸發(fā)后 n 毫秒后執(zhí)行,如果在 n 毫秒內(nèi)又觸發(fā)了事件,則會重新計時

    /**
    * @desc 函數(shù)防抖
    * @param func 回調(diào)函數(shù)
    * @param delay 延遲執(zhí)行毫秒數(shù)
    */
    function debounce(func, delay) {
        let timer;  // 定時器
    
        return function () { 
            let context = this;  // 記錄 this 值,防止在回調(diào)函數(shù)中丟失
            let args = arguments;  // 函數(shù)參數(shù)
    
            //如果定時器存在,則清除定時器(如果沒有,也沒必要進行處理)
            timer ? clearTimeout(timer) : null; 
    
            timer = setTimeout(() => { 
                // 防止 this 值變?yōu)?nbsp;window
                func.apply(context, args) 
            }, delay);
        }
    }
    (2)版本2

    觸發(fā)事件后立即執(zhí)行回調(diào)函數(shù),但是觸發(fā)后n毫秒內(nèi)不會再執(zhí)行回調(diào)函數(shù),如果 n 毫秒內(nèi)觸發(fā)了事件,也會重新計時。

     /**
    * @desc 函數(shù)防抖
    * @param func 回調(diào)函數(shù)
    * @param delay 延遲執(zhí)行毫秒數(shù)
    */ 
    function _debounce(func, delay) {
        let timer;  // 定時器
    
        return function () {
            let context = this;  // 記錄 this 值,防止在回調(diào)函數(shù)中丟失
            let args = arguments;  // 函數(shù)參數(shù)
    
            // 標識是否立即執(zhí)行
            let isImmediately = !timer;
    
            //如果定時器存在,則清除定時器(如果沒有,也沒必要進行處理)
            timer ? clearTimeout(timer) : null;
    
            timer = setTimeout(() => {
                timer = null;
            }, delay);
    
            // isImmediately 為 true 則 執(zhí)行函數(shù)(即首次觸發(fā)事件)
            isImmediately ? func.apply(context, args) : null;
        }
    }

    舉個例子來對比一下兩個版本的區(qū)別:

    document.body.onclick= debounce(function () { console.log('hello') },1000)

    如上代碼中,我們給body添加了一個點擊事件監(jiān)聽器。

    • 如果是版本1的防抖函數(shù),當(dāng)我點擊body時,控制臺不會立即打印hello,要等 1000ms 后才會打印。在這 1000s 內(nèi)如果還點擊了 body,那么就會重新計時。即最后一次點擊 body 過1000ms后控制臺才會打印hello

    • 如果是版本2的防抖函數(shù),當(dāng)我首次點擊body時,控制臺會立馬打印 hello,但是在此之后的 1000ms 內(nèi)點擊 body ,控制臺不會有任何反應(yīng)。在這 1000s 內(nèi)如果還點擊了 body,那么就會重新計時。必須等計時結(jié)束后再點擊body,控制臺才會再次打印 hello。

    1.2 防抖的實際應(yīng)用

    (1)搜索框建議項

    通常,搜索框會提供下拉菜單,為用戶當(dāng)前的輸入提供自動完成選項。但有時建議項是通過請求后端得到的??梢栽趯崿F(xiàn)提示文本時應(yīng)用防抖,在等待用戶停止輸入一段時間后再顯示建議文本。因此,在每次擊鍵時,都會等待幾秒鐘,然后再給出建議。

    (2)消除resize事件處理程序的抖動。

    window 觸發(fā) resize 的時候,不斷的調(diào)整瀏覽器窗口大小會不斷觸發(fā)這個事件,用防抖讓其只觸發(fā)一次

    (3)自動保存

    例如掘金一類的網(wǎng)站,都會內(nèi)嵌文本編輯器,在編輯過程中會自動保存文本,防止數(shù)據(jù)丟失。每次保存都會與后端進行數(shù)據(jù)交互,所以可以應(yīng)用防抖,在用戶停止輸入后一段時間內(nèi)再自動保存。

    (4)手機號、郵箱等輸入驗證檢測

    通常對于一些特殊格式的輸入項,我們通常會檢查格式。我們可以應(yīng)用防抖在用戶停止輸入后一段時間再進行格式檢測,而不是輸入框中內(nèi)容發(fā)生改變就檢測。

    (5)在用戶停止輸入之前不要發(fā)出任何 Ajax 請求

    二、節(jié)流(Throttle)

    節(jié)流:無論用戶觸發(fā)事件多少次,附加的函數(shù)在給定的時間間隔內(nèi)只會執(zhí)行一次。(即:回調(diào)函數(shù)在規(guī)定時間內(nèi)最多執(zhí)行一次)

    JavaScript防抖與節(jié)流怎么使用

     例如,當(dāng)用戶單擊一個按鈕時,會執(zhí)行一個在控制臺上打印Hello, world的函數(shù)。現(xiàn)在,假設(shè)對這個函數(shù)應(yīng)用 1000 毫秒的限制時,無論用戶點擊按鈕多少次,Hello, world在 1000 毫秒內(nèi)都只會打印一次。節(jié)流可確保函數(shù)定期執(zhí)行。

    2.1 節(jié)流函數(shù)的實現(xiàn)

    (1)版本1 —— 使用定時器
     /**
    * @desc 函數(shù)節(jié)流
    * @param func 回調(diào)函數(shù) 
    * @param limit 時間限制
    */
    const throttle = (func, limit) => {
        let inThrottle;  // 是否處于節(jié)流限制時間內(nèi)
    
        return function() {
            const context = this;
            const args = arguments;
    
            // 跳出時間限制
            if (!inThrottle) {
                func.apply(context, args);  // 執(zhí)行回調(diào)
                inThrottle = true;  
                // 開啟定時器計時
                setTimeout(() => inThrottle = false, limit);
            }
        }
    }
    (2)版本2 —— 計算當(dāng)前時間與上次執(zhí)行函數(shù)時間的間隔
     /**
    * @desc 函數(shù)節(jié)流
    * @param func 回調(diào)函數(shù)
    * @param limit 時間限制
    */ 
    function throttle(func, limit) {
        //上次執(zhí)行時間
        let previous = 0;
        return function() {
            //當(dāng)前時間
            let now = Date.now();
    
            let context = this;
            let args = arguments;
    
            // 若當(dāng)前時間-上次執(zhí)行時間大于時間限制
            if (now - previous > limit) {
                func.apply(context, args);
                previous = now;
            }
        }
    }

    很多現(xiàn)有的庫中已經(jīng)實現(xiàn)了防抖函數(shù)和節(jié)流函數(shù)

    2.2 節(jié)流的實際應(yīng)用

    (1)游戲中通過按下按鈕執(zhí)行的關(guān)鍵動作(例如:射擊、平A)

    拿王者榮耀為例,通常都有攻速一說。如果攻速低,即使 n 毫秒內(nèi)點擊平A按鈕多次,也只會執(zhí)行一次平A。其實這里就類似于節(jié)流的思想,可以通過設(shè)置節(jié)流的時間間隔限制,來改變攻速。

    (2)滾動事件處理

    如果滾動事件被觸發(fā)得太頻繁,可能會影響性能,因為它包含大量視頻和圖像。因此滾動事件必須使用節(jié)流

    (3)限制mousemove/touchmove事件處理程序

    小結(jié)

    如何選擇防抖和節(jié)流:

    關(guān)于防抖函數(shù)和節(jié)流函數(shù)的選擇,一篇博客中是這樣建議的:

    A debounce is utilized when you only care about the final state. A throttle is best used when you want to handle all intermediate states but at a controlled rate.

    即:如果只關(guān)心最終狀態(tài),建議使用防抖。如果是想要函數(shù)以可控的速率執(zhí)行,那么建議使用節(jié)流。

    延時多久合理:

    • 大多數(shù)屏幕的刷新頻率是每秒60Hz,瀏覽器的渲染頁面的標準幀率也為60FPS,瀏覽器每秒會重繪60次,而每幀之間的時間間隔是DOM視圖更新的最小間隔。

    • 一個平滑而流暢的動畫,最佳的循環(huán)間隔即幀與幀的切換時間希望是 16.6ms(1s/60)內(nèi),也意味著17ms內(nèi)的多次DOM改動會被合并為一次渲染。

    • 當(dāng)執(zhí)行回調(diào)函數(shù)時間大于16.6ms(系統(tǒng)屏幕限制的刷新頻率),UI將會出現(xiàn)丟幀(即UI這一刻不會被渲染),且丟幀越多,引起卡頓情況更嚴重。

    JavaScript防抖與節(jié)流怎么使用

    感謝各位的閱讀,以上就是“JavaScript防抖與節(jié)流怎么使用”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對JavaScript防抖與節(jié)流怎么使用這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關(guān)知識點的文章,歡迎關(guān)注!

    向AI問一下細節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI