1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        性能優(yōu)化小冊(cè) - React 搜索優(yōu)化:防抖、緩存、LRU

        共 3628字,需瀏覽 8分鐘

         ·

        2021-01-31 02:47

        最近要主導(dǎo) react 項(xiàng)目重構(gòu)優(yōu)化等相關(guān)的工作,由于有好長(zhǎng)時(shí)間沒(méi)碰 React 了,今天索性把一個(gè)基于關(guān)鍵字搜索的 demo 做一下簡(jiǎn)單優(yōu)化,在此記錄一下。

        主要從三個(gè)方面進(jìn)行優(yōu)化處理:


          1. 減少事件的觸發(fā)頻率 - 對(duì)關(guān)鍵字鍵入進(jìn)行?debounce?處理

          1. 減少 HTTP 請(qǐng)求 - 對(duì)重復(fù)的 HTTP 請(qǐng)求進(jìn)行緩存攔截

          1. 緩存淘汰策略 - 使用 LRU 優(yōu)化緩存

        減少事件的觸發(fā)頻率 - debounce

        debounce?旨在時(shí)間段內(nèi)控制事件只在最后一次操作觸發(fā)。

        debounce?原理:是維護(hù)一個(gè)計(jì)時(shí)器,在規(guī)定的?delay?時(shí)間后觸發(fā)函數(shù),在?delay?時(shí)間內(nèi)再次觸發(fā)的話,就會(huì)取消之前的計(jì)時(shí)器而重新設(shè)置。這樣一來(lái),只有最后一次操作能被觸發(fā)。

        下面是 react 中?debounce?優(yōu)化的代碼:

        ...
        handler?=?e?=>?{
        ??let?val?=?e.target.value;
        ??if(val)?{?
        ????this.search(val);
        ??}
        ??this.setState(()?=>?({
        ????value:?e.target.value
        ??}))
        }

        debounce?=?(fn,?delay)?=>?{
        ??let?timer?=?null;
        ??return?function(event)?{
        ????timer?&&?clearTimeout(timer);
        ????event.persist?&&?event.persist()?//?保留引用,已備異步階段訪問(wèn)
        ????timer?=?setTimeout(()?=>?{
        ??????fn.call(this,?event)
        ????},?delay)?
        ??}
        }

        onChangeHandler?=?this.debounce(this.handler,?1000)
        ...
        render()?{
        ??return?(
        ????<div>
        ??????<input
        ????????//?這里不能設(shè)置成?value
        ????????defaulValue={this.state.value}
        ????????onChange={e?=>
        ?this.onChangeHandler(e)}
        ????????placeholder="試著輸入一些文字"
        ??????/>
        ??????<div>
        ????????<Suspense?fallback="Loading">
        ??????????{this.renderMovies}
        ????????Suspense>

        ??????div>
        ????div>
        ??);
        }

        這里需要注意的是:?如果想要異步訪問(wèn)合成事件對(duì)象 SyntheticEvent,需要調(diào)用?persist()?方法或者對(duì)事件對(duì)象進(jìn)行深拷貝?const event = { ...event }?保留對(duì)事件的引用。

        在 React 事件調(diào)用時(shí),React 傳遞給事件處理程序是一個(gè)合成事件對(duì)象的實(shí)例 SyntheticEvent 是通過(guò)合并得到的。這意味著在事件回調(diào)被調(diào)用后,SyntheticEvent 對(duì)象將被重用并且所有屬性都將被取消。這是出于性能原因,因此,您無(wú)法以異步方式訪問(wèn)該事件。React合成事件官方文檔

        event.persist()
        //?or
        const?event:?SyntheticEvent?=?{?...event?}

        還有一個(gè)隱晦點(diǎn)的需要指出,?我們知道如果想要使?input?為受控元素,正確的做法是:在給?input?綁定?value?時(shí),需要同時(shí)綁定?onChange?事件來(lái)監(jiān)聽(tīng)數(shù)據(jù)變化,否則就會(huì)報(bào)如下警告。

        但是當(dāng)你異步傳遞?SyntheticEvent?對(duì)象時(shí),使用?value?屬性進(jìn)行綁定的?input,值不會(huì)再發(fā)生變化(但它仍是一個(gè)受控元素)。

        ...
        event.persist()
        timer?=?setTimeout(()?=>?{
        ??fn.call(this,?event)?//?傳遞?event
        },?delay)?
        ...
        ??defaultValue={this.state.value}
        ??//?value={this.state.value}?使用?value?屬性,值不會(huì)發(fā)生變化
        ??onChange={e?=>?this.onChangeHandler(e)}
        />

        如下圖:

        減少 HTTP 請(qǐng)求

        減少 HTTP 請(qǐng)求的手段之一就是將 HTTP 請(qǐng)求結(jié)果進(jìn)行緩存,如果下次請(qǐng)求的?url?未發(fā)生變化,則直接從緩存中獲取數(shù)據(jù)。

        import?axios?from?'axios';
        const?caches?=?{};?
        const?axiosRequester?=?()?=>?{
        ??let?cancel;
        ??return?async?url?=>?{
        ????if(cancel)?{
        ??????cancel.cancel();
        ????}
        ????cancel?=?axios.CancelToken.source();
        ????try?{
        ??????if(caches[url])?{?//如果請(qǐng)求的?url?之前已經(jīng)提交過(guò),就不在進(jìn)行請(qǐng)求,返回之前請(qǐng)求回來(lái)的數(shù)據(jù)
        ????????return?caches[url];
        ??????}
        ??????const?res?=?await?axios.post(url,?{
        ?????????cancelToken:?cancel.token
        ??????})
        ??????const?result?=?res.data.result;
        ??????caches[url]?=?result;??//將?url作為?key,?result?為請(qǐng)求回來(lái)的數(shù)據(jù),存儲(chǔ)起來(lái)
        ??????return?result;
        ????}?catch(error)?{
        ??????if(axios.isCancel(error))?{
        ????????console.log('Request?canceled',?error.message);
        ??????}?else?{
        ????????console.log(error.message);
        ??????}
        ????}
        ??}
        }

        export?const?_search?=?axiosRequester();

        在使用?axios?進(jìn)行 HTTP 請(qǐng)求時(shí),首先根據(jù)?url?判斷數(shù)據(jù)是否已被緩存,如果命中則直接從緩存中拿數(shù)據(jù)。如果未被緩存,則發(fā)起?HTTP?請(qǐng)求,并將請(qǐng)求回來(lái)的結(jié)果以鍵值對(duì)的形式保存在?caches?對(duì)象中。

        緩存淘汰策略 - LRU

        由于緩存空間是有限的,所以不能無(wú)限制的進(jìn)行數(shù)據(jù)存儲(chǔ),當(dāng)存儲(chǔ)容量達(dá)到一個(gè)閥值時(shí),就會(huì)造成內(nèi)存溢出,因此在進(jìn)行數(shù)據(jù)緩存時(shí),就要根據(jù)情況對(duì)緩存進(jìn)行優(yōu)化,清除一些可能不會(huì)再用到的數(shù)據(jù)。

        這里我們用到 keepAlive 相同的緩存淘汰機(jī)制 - LRU。

        LRU - 最近最少使用策略

        • 以時(shí)間作為參考,如果數(shù)據(jù)最近被訪問(wèn)過(guò),那么將來(lái)被訪問(wèn)的幾率會(huì)更高,如果以一個(gè)數(shù)組去記錄數(shù)據(jù),當(dāng)有一數(shù)據(jù)被訪問(wèn)時(shí),該數(shù)據(jù)會(huì)被移動(dòng)到數(shù)組的末尾,表明最近被使用過(guò),當(dāng)緩存溢出時(shí),會(huì)刪除數(shù)組的頭部數(shù)據(jù),即將最不頻繁使用的數(shù)據(jù)移除。

        實(shí)現(xiàn) LRU 策略我們需要一個(gè)存儲(chǔ)緩存對(duì)象?key?的數(shù)組:

        const?keys?=?[];

        并且需要設(shè)置一個(gè)閥值,控制緩存棧最大的存儲(chǔ)數(shù)量:

        const?MAXIMUN_CACHES?=?20;

        還需要一個(gè)用來(lái)刪除數(shù)組?keys?成員項(xiàng)的工具函數(shù)?remove

        function?remove(arr,?item)?{
        ??if?(arr.length)?{
        ????var?index?=?arr.indexOf(item)
        ????if?(index?>?-1)?{
        ??????return?arr.splice(index,?1)
        ????}
        ??}
        }

        最后再實(shí)現(xiàn)一個(gè)?pruneCacheEntry?函數(shù),用來(lái)刪除最少訪問(wèn)的數(shù)據(jù)(第一項(xiàng)):

        //?傳入?keys?數(shù)組的第一項(xiàng)
        if?(keys.length?>?parseInt(MAXIMUN_CACHES))?{
        ??pruneCacheEntry(caches,?keys[0],?keys);
        }

        ...
        //?刪除最少訪問(wèn)的數(shù)據(jù)
        function?pruneCacheEntry?(?caches,?key,?keys)?{
        ??caches[key]?=?null;?//?清空對(duì)應(yīng)的數(shù)據(jù)
        ??delete?caches[key];?//?刪除緩存?key
        ??remove(keys,?key);
        }

        最終「鍵入防抖」結(jié)合 LRU 緩存優(yōu)化后的搜索功能就像這樣:

        同系列文章:

        • 性能優(yōu)化小冊(cè) - 異步堆棧追蹤:為什么 await 勝過(guò) Promise
        • 性能優(yōu)化小冊(cè) - 分類構(gòu)建:利用好 webpack hash
        • 性能優(yōu)化小冊(cè) - 提高網(wǎng)頁(yè)響應(yīng)速度:優(yōu)化你的 CDN 性能
        • 性能優(yōu)化小冊(cè) - 可編程式緩存:Service Workers
        • 性能優(yōu)化小冊(cè) - 讓頁(yè)面更早的渲染:使用 preload 提升資源加載優(yōu)先級(jí)

        關(guān)注公眾號(hào)「前端UpUp」,聯(lián)系作者?? 「DayDay2021」,持續(xù)更新好文章.


        瀏覽 88
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            国产女同2互磨高潮在线观看 | 欧美成人三级在线观看 | 性高潮动态图片 | 丁香五月综合网 | 久久人人摸 | 亚洲中文字幕在线观看免费视频 | 特级西西西西4444级酉西88wwww特 | 最新av| 国产边做饭边被躁bd | 靠逼无码 |