<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

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

          共 3628字,需瀏覽 8分鐘

           ·

          2021-01-31 02:47

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

          主要從三個方面進行優(yōu)化處理:


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

            1. 減少 HTTP 請求 - 對重復(fù)的 HTTP 請求進行緩存攔截

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

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

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

          debounce?原理:是維護一個計時器,在規(guī)定的?delay?時間后觸發(fā)函數(shù),在?delay?時間內(nèi)再次觸發(fā)的話,就會取消之前的計時器而重新設(shè)置。這樣一來,只有最后一次操作能被觸發(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()?//?保留引用,已備異步階段訪問
          ????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>
          ??);
          }

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

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

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

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

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

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

          如下圖:

          減少 HTTP 請求

          減少 HTTP 請求的手段之一就是將 HTTP 請求結(jié)果進行緩存,如果下次請求的?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])?{?//如果請求的?url?之前已經(jīng)提交過,就不在進行請求,返回之前請求回來的數(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?為請求回來的數(shù)據(jù),存儲起來
          ??????return?result;
          ????}?catch(error)?{
          ??????if(axios.isCancel(error))?{
          ????????console.log('Request?canceled',?error.message);
          ??????}?else?{
          ????????console.log(error.message);
          ??????}
          ????}
          ??}
          }

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

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

          緩存淘汰策略 - LRU

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

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

          LRU - 最近最少使用策略

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

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

          const?keys?=?[];

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

          const?MAXIMUN_CACHES?=?20;

          還需要一個用來刪除數(shù)組?keys?成員項的工具函數(shù)?remove

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

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

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

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

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

          同系列文章:

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

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


          瀏覽 88
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲天堂小说视频 | 婷婷五月天97干 | 蜜乳一区二区三区 | 欧美一级AA大片免费看视频 | 激情国产福利 |