最近要對 react 項目作重構優化等相關的工做,因爲有好長時間沒碰 React 了,今天索性把一個基於關鍵字搜索的 demo 作一下簡單優化,在此記錄如下。html
主要從三個方面進行優化處理:react
debounce
處理debounce
旨在時間段內控制事件只在最後一次操做觸發。webpack
debounce
原理:是維護一個計時器,在規定的 delay
時間後觸發函數,在 delay
時間內再次觸發的話,就會取消以前的計時器而從新設置。這樣一來,只有最後一次操做能被觸發。ios
下面是 react 中 debounce
優化的代碼:web
... 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 // 這裏不能設置成 value defaulValue={this.state.value} onChange={e => this.onChangeHandler(e)} placeholder="試着輸入一些文字" /> <div> <Suspense fallback="Loading"> {this.renderMovies} </Suspense> </div> </div> ); }
這裏須要注意的是:若是想要異步訪問合成事件對象 SyntheticEvent,須要調用 persist()
方法或者對事件對象進行深拷貝 const event = { ...event }
保留對事件的引用。編程
在 React 事件調用時,React 傳遞給事件處理程序是一個合成事件對象的實例 SyntheticEvent 是經過合併獲得的。 這意味着在事件回調被調用後,SyntheticEvent 對象將被重用而且全部屬性都將被取消。 這是出於性能緣由,所以,您沒法以異步方式訪問該事件。 React合成事件官方文檔
event.persist() // or const event: SyntheticEvent = { ...event }
還有一個隱晦點的須要指出,咱們知道若是想要使 input
爲受控元素,正確的作法是:在給 input
綁定 value
時,須要同時綁定 onChange
事件來監聽數據變化,不然就會報以下警告。axios
可是當你異步傳遞 SyntheticEvent
對象時,使用 value
屬性進行綁定的 input
,值不會再發生變化(但它還是一個受控元素)。segmentfault
... event.persist() timer = setTimeout(() => { fn.call(this, event) // 傳遞 event }, delay) ... <input defaultValue={this.state.value} // value={this.state.value} 使用 value 屬性,值不會發生變化 onChange={e => this.onChangeHandler(e)} />
以下圖:數組
減小 HTTP 請求的手段之一就是將 HTTP 請求結果進行緩存,若是下次請求的 url
未發生變化,則直接從緩存中獲取數據。緩存
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 以前已經提交過,就不在進行請求,返回以前請求回來的數據 return caches[url]; } const res = await axios.post(url, { cancelToken: cancel.token }) const result = res.data.result; caches[url] = result; //將 url做爲 key, result 爲請求回來的數據,存儲起來 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 請求時,首先根據 url
判斷數據是否已被緩存,若是命中則直接從緩存中拿數據。若是未被緩存,則發起 HTTP
請求,並將請求回來的結果以鍵值對的形式保存在 caches
對象中。
因爲緩存空間是有限的,因此不能無限制的進行數據存儲,當存儲容量達到一個閥值時,就會形成內存溢出,所以在進行數據緩存時,就要根據狀況對緩存進行優化,清除一些可能不會再用到的數據。
這裏咱們用到 keepAlive 相同的緩存淘汰機制 - LRU。
LRU - 最近最少使用策略
實現 LRU 策略咱們須要一個存儲緩存對象 key
的數組:
const keys = [];
而且須要設置一個閥值,控制緩存棧最大的存儲數量:
const MAXIMUN_CACHES = 20;
還須要一個用來刪除數組 keys
成員項的工具函數 remove
:
function remove(arr, item) { if (arr.length) { var index = arr.indexOf(item) if (index > -1) { return arr.splice(index, 1) } } }
最後再實現一個 pruneCacheEntry
函數,用來刪除最少訪問的數據(第一項):
// 傳入 keys 數組的第一項 if (keys.length > parseInt(MAXIMUN_CACHES)) { pruneCacheEntry(caches, keys[0], keys); } ... // 刪除最少訪問的數據 function pruneCacheEntry ( caches, key, keys) { caches[key] = null; // 清空對應的數據 delete caches[key]; // 刪除緩存 key remove(keys, key); }
最終「鍵入防抖」結合 LRU 緩存優化後的搜索功能就像這樣:
同系列文章: