成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

通過函數(shù)節(jié)流與函數(shù)分時提升應用性能

lijy91 / 764人閱讀

摘要:結束語在這里,我們雖然僅僅涉及了一些高階函數(shù)應用的皮毛,但這兩個技巧,實是項目開發(fā)當中克敵制勝,提高性能的實戰(zhàn)利器。

通過函數(shù)節(jié)流與函數(shù)分時提升應用性能

在例如表單自動補全,數(shù)據(jù)埋點,文章內容自動保存,視口監(jiān)聽,拖拽,列表渲染等高頻操作時,如果同時有其它UI行為占據(jù)線程,瀏覽器端時常會出現(xiàn)卡頓現(xiàn)象,服務器端也面臨著較大壓力。這時,函數(shù)節(jié)流,與函數(shù)分時將成為我們的一大輔助。

一、函數(shù)節(jié)流

看一則自動補全的例子

//自動補全
const input       = document.querySelector("#autocompleteSearch"),
      completeArr = []; //需要被渲染的補全提示數(shù)據(jù)

input.addEventListener("keydown", e => {
    
    const value = e.currentTarget.value,
          xhr   = new XMLHttpRequest();
    
    
    xhr.addEventListener("load", ()=> {
        //請求完成后,將數(shù)據(jù)裝載到數(shù)組中
        xhr.status === 200 && completeArr.push(xhr.responseText);
    });
    
    xhr.open("GET", "http://api.com");
    xhr.send(value);
    
});

在這里,我沒有提供具體的UI層的操作,只提供了觸發(fā)事件時的handler,實際的開發(fā)中可能還需要涉及要補全數(shù)據(jù)數(shù)組的渲染邏輯以及排序和清除邏輯,但這并不妨礙我們理解本問題的過程。

可以看到的是,為了實時更新補全數(shù)據(jù),每次當用戶按下按鍵時,我們都要向服務器去發(fā)起一次請求。如果產(chǎn)品的用戶基數(shù)很大,并發(fā)一高,那就實在是有些坑后端隊友了。

回想需求,我們要根據(jù)用戶輸入的關鍵字像服務器索取補全的字段,反饋給用戶快速選擇。

實際上,在用戶輸入表單的過程中,可能按下很多次按鍵才會打出一個字,或者是打出了很多個字后,才能檢索出真整的數(shù)據(jù)。

基于這個角度來換一下思路,如何限制請求的發(fā)送呢?

判斷value的長度,輸入兩個三個字以上,再向服務器發(fā)起請求

將事件的handler觸發(fā)頻率降低

第一種思路,不失為是一種可行的方案,但是很難復用,而且用戶真實想要搜入的字數(shù)并不確定。

第二種思路,既能限制頻率,減少請求,還能近實時向用戶反饋,無視用戶輸入的字符串長度,還可以實現(xiàn)高復用。

下面提供實現(xiàn)的方式,首先,實現(xiàn)函數(shù)節(jié)流:

const throttle =  (fn, time = 1000)=> {
    let triggered = true,        // 首次觸發(fā)狀態(tài)的標識
        timer;                  // 定時器觸發(fā)標識
    return function () {
        if (triggered) {
            // 首次觸發(fā) 回調直接執(zhí)行
            fn.apply(this, arguments);
            //執(zhí)行后 使首次觸發(fā)標識為假
            return triggered = false;
        }
        if (timer) {
            // 定時器標識 如果為真 代表著之前的分流限制范圍 尚未結束
            return false;
        }
        timer = setInterval(()=> {
            //如果定時器標識不為真 則為定時器賦上引用
            clearInterval(timer);
            // 取反定時器標識
            timer = !timer;
            // 執(zhí)行回調
            fn.apply(self, arguments);
        }, time)
    }
    
};

上述代碼,利用了閉包與高階函數(shù),限制了函數(shù)的觸發(fā),關鍵點在于首次觸發(fā)與之前的節(jié)流是否結束的判斷。

改造一下上面的自動補全代碼。

const input          = document.querySelector("#autocompleteSearch"),
      completeArr    = [],
      keydownHandler = throttle(e => {
    
          const value = e.currentTarget.value,
                xhr   = new XMLHttpRequest();
    
    
          xhr.addEventListener("load",()=> {
              //請求完成后,將數(shù)據(jù)裝載到數(shù)組中
              xhr.status === 200 && completeArr.push(xhr.responseText);
          });
    
          xhr.open("GET", "http://api.com");
          xhr.send(value);
    
      }); //需要被渲染的補全提示數(shù)據(jù)

input.addEventListener("keydown",keydownHandler);


function throttle(fn, time = 1000) {
    let triggered = true,        // 首次觸發(fā)狀態(tài)的標識
        timer;                  // 定時器觸發(fā)標識
    return function () {
        if (triggered) {
            // 首次觸發(fā) 回調直接執(zhí)行
            fn.apply(this, arguments);
            //執(zhí)行后 使首次觸發(fā)標識為假
            return triggered = false;
        }
        if (timer) {
            // 定時器標識 如果為真 代表著之前的分流限制范圍 尚未結束
            return false;
        }
        timer = setInterval(()=> {
            //如果定時器標識不為真 則為定時器賦上引用
            clearInterval(timer);
            // 取反定時器標識
            timer = !timer;
            // 執(zhí)行回調
            fn.apply(self, arguments);
        }, time)
    }
    
}

如此,實現(xiàn)了keydown事件觸發(fā)的頻率,當然,一些其他高頻的事件回調依舊適合,我們可以根據(jù)具體的業(yè)務場景,來傳入合理的time值,達到節(jié)流,既減輕了服務器端的壓力,又提升了性能,例如上面的自動補全,1秒的延遲,用戶幾乎感受不到,何樂而不為呢?

二、分時函數(shù)

上面那種隱藏在用戶操作背后,節(jié)流函數(shù)是一個很好的解決方案。同時,我們可能會面臨另外一種場景,即是一次性渲染。

比如說,我們有這樣的需求,后臺給了我們2000行記錄的數(shù)據(jù),要一次性用列表全部渲染出來。

2000行數(shù)據(jù)可不是一個小數(shù)目,如果里面內嵌了很多子節(jié)點邏輯,那么很有可能我們也許要渲染上萬個節(jié)點,眾所周知,DOM可是瀏覽器環(huán)境性能的最大損耗者。為了提升用戶體驗與性能,通常情況下,我會使用兩種操作。

使用DOM的fragment,避免每次節(jié)點生成時的反復插入,可以在合理的時機向相應的節(jié)點插入,這方面的資料很多,可以自行查閱。

使用函數(shù)分時 來分批處理渲染邏輯

先看如何在不分時的情況下操作節(jié)點:

const
    list        = document.querySelector("#ul"),
    virtualList = document.createDocumentFragment(), // 虛擬dom容器
    listArr     = [
        {text: "hello react!"}
        // 假設這里有2000條記錄
    ];

for (let i of listArr) {
    // 使用for of 遍歷數(shù)據(jù)
    const li       = document.createElement("li");
    li.textContent = i;
    // 插入虛擬容器中
    virtualList.appendChild(li);
    
}

// 把載滿節(jié)點的虛擬容器 插入到真實的列表元素中
list.appendChild(virtualList);

再來看分函數(shù)分時的實現(xiàn):

function chunkFunc({fn, arr, count = arr.length, time = 200, sCb, aCb}) {
    
    /*
     * @params
     * fn   : 需要被分時的處理邏輯
     * arr  : 全部的業(yè)務數(shù)據(jù)
     * count: 每次分時的具體數(shù)量
     *        假設總共2000條數(shù)據(jù)
     *        我們可以設定
     *        每次分成200條執(zhí)行
     *        默認為業(yè)務數(shù)據(jù)的長度
     * time : 分時的時間間隔 默認200 毫秒
     * sCb  : singleCallback 每次分時遍歷結束時執(zhí)行的回調
     * aAb  : allCallback 全部遍歷結束時需要做的回調
     * */
    
    let
        timer,      // 用以分時的定時器標識
        start;      // 遍歷處理邏輯
    
    start = () => {
        for (let i = 0; i < count; i++) {
            //如果count給了值 我們循環(huán)count次 每次循環(huán)都從業(yè)務數(shù)據(jù)里取值 然后執(zhí)行處理邏輯
            fn(arr.shift());
        }
        //分時遍歷結束 如果有回調 執(zhí)行回調
        sCb && sCb();
    };
    
    return () => {
        // 默認每200毫秒執(zhí)行一次
        timer = setInterval(function () {
            // 如果原始數(shù)據(jù)被取空了 則停止執(zhí)行
            if (arr.length === 0) {
                aCb && aCb();
                return clearInterval(timer)
            }
            // 不然 執(zhí)行遍歷邏輯
            start();
        }, time);
    }
}

實現(xiàn)方式很簡單,即根據(jù)用戶給定的分時單位與時間,利用定時器重新包裝用戶處理邏輯,這里我們需要將渲染邏輯稍微改動,抽離出遍歷邏輯,添加遍歷結束回調方法(可選)。

重構代碼如下:

const
    list    = document.querySelector("#ul"),
    listArr = [
        {text: "hello react!"}
        // 假設這里有2000條記錄
    ];

let virtualDOM = document.createDocumentFragment();


chunkFunc({
    fn(data) {
        // 生成節(jié)點邏輯
        const li       = document.createElement("li");
        li.textContent = data.text;
        virtualDOM.appendChild(li);
    },
    sCb() {
        // 分時遍歷結束 將虛擬節(jié)點 插入LIST
        list.appendChild(virtualDOM);
        // 重置虛擬節(jié)點 避免重復生成節(jié)點
        virtualDOM = document.createDocumentFragment();
    },
    aCb() {
        // 最終結束后 解除引用
        virtualDOM = null;
    },
    arr  : listArr,
    count: 8,
    time : 300,
})();

通過抽離了插入、生成節(jié)點的邏輯、給出不同階段的回調,我們成功的將本來需要一次性生成的節(jié)點,分批生成,提高了性能和用戶體驗。

結束語

在這里,我們雖然僅僅涉及了一些高階函數(shù)應用的皮毛,但這兩個技巧,實是項目開發(fā)當中克敵制勝,提高性能的實戰(zhàn)利器。根據(jù)不同的業(yè)務場景伸縮,我們可以衍生出不同的方法。如果能結合單例模式,代理模式等常用的設計模式,將會有更為廣擴的發(fā)揮。

文章版權歸作者所有,未經(jīng)允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉載請注明本文地址:http://m.hztianpu.com/yun/91993.html

相關文章

  • JavaScript 設計模式開發(fā)實踐讀書筆記

    摘要:設計模式與開發(fā)實踐讀書筆記最近利用碎片時間在上面閱讀設計模式與開發(fā)實踐讀書這本書,剛開始閱讀前兩章內容,和大家分享下我覺得可以在項目中用的上的一些筆記。事件綁定暫時這么多,以后會不定期更新一些關于我讀這本書的筆記內容 JavaScript 設計模式與開發(fā)實踐讀書筆記 最近利用碎片時間在 Kindle 上面閱讀《JavaScript 設計模式與開發(fā)實踐讀書》這本書,剛開始閱讀前兩章內容,...

    FingerLiu 評論0 收藏0
  • 如何使用函數(shù)來優(yōu)化性能?

    摘要:原理連續(xù)觸發(fā)事件,但是事件函數(shù)只在在規(guī)定的周期之內只執(zhí)行一次。代碼實現(xiàn)使用在使用節(jié)流函數(shù)后,我們在暫停輸入的后就會輸入輸入框內的值,暫停時間小于,則不會輸出,將重新計算函數(shù)執(zhí)行時間。使用分時函數(shù)這樣在調用分時函數(shù)后每隔創(chuàng)建個節(jié)點。 一、節(jié)流函數(shù) 1. 使用場景 DOM.onclick()事件,我們給一個DOM節(jié)點綁定了點擊事件,當點擊該元素時觸發(fā)事件函數(shù)的執(zhí)行,但是當我們頻繁點擊該元素...

    lncwwn 評論0 收藏0
  • JavaScript中高階函數(shù)的魅力

    摘要:上傳進度下面通過高階函數(shù)的方式我們來實現(xiàn)函數(shù)節(jié)流節(jié)流函數(shù)計時器是否是第一次調用首次調用直接放行存在計時器就攔截設置使用節(jié)流分時函數(shù)節(jié)流函數(shù)為我們提供了一種限制函數(shù)被頻繁調用的解決方案。 高階函數(shù)是指至少滿足下列條件之一的函數(shù) 1:函數(shù)可以作為參數(shù)被傳遞 2:函數(shù)可以作為返回值輸出 JavaScript語言中的函數(shù)顯然的是滿足了高階函數(shù)的條件,下面我們一起來探尋JavaScript種高階...

    Tony_Zby 評論0 收藏0
  • js編程中經(jīng)常遇到的一些問題(持續(xù)更新)

    摘要:一前言本文適合有一定開發(fā)基礎的讀者,文章涉及開發(fā)中經(jīng)常遇到的一些令人疑惑的問題,理解這些問題有助于我們快速提升對這門語言的理解和應用能力。 一:前言 本文適合有一定JS開發(fā)基礎的讀者,文章涉及開發(fā)中經(jīng)常遇到的一些令人疑惑的問題,理解這些問題有助于我們快速提升對JS這門語言的理解和應用能力。文章只講述具體問題中的關鍵問題,不涵蓋全面的知識點。如想了解具體的知識,可以參考筆者博客的相關文章...

    willin 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<