React.memo
當16.6的memo問世,函數組件就有了類似PuerComponent和shouldComponentUpdate的解決方案,memo的使用方法:
const C = (props) => { return那一夜{props.name}真美
} export default React.memo(C)
當父組件執行render的時候,避免不了C組件的渲染和C函數的執行(如果不在外面判斷的話{isShowC && })。當到了C組件的時候,會淺比較C組件前後props值。如果props每一個屬性值都一樣,會跳過函數組件C的執行,減少了不必要的渲染,達到了性能優化。
memo第二個參數
第二個參數,是一個函數,該函數傳入參數是新props和上次props,我們可以在函數里面做判斷邏輯,控制返回值。當我們讓函數return true的時候,告訴react這兩個props是一樣的,javascript不用重新執行整個函數組件。反之false的時候會重新執行該組件
memo(IfEqula,() => false);
比如這行代碼,判斷函數一致返回false,memo包住的IfEqula組件無論怎樣都會重新執行 當我們用上了memo,就可以根據業務來進行優化了:
React.memo(C,(nextProps,prevProps) => { // 做我們想做的事情,類似shouldComponentUpdate })
函數組件中傳入props值為函數時
我們都知道,js中函數不是簡單數據類型,也就是說function(){}和function(){}是不一樣的,與{}和{}不一樣同理。那麼我們傳入props.onClick(即使是長得一樣的內容完全一樣),前後props.onClick都不能劃上等號
{}}/>
覺得inline function不好看,那前面定義一下,實際上還是逃不了同一件事情:它們是不一樣的。這是因為,函數組件的渲染,也就是執行,每一次重新執行,函數作用域裡面一切都是重新開始。這就相當與上一次組件渲染const handelClick = () =>,後面渲染又一次const handleClick = () => {} ,它們都不是同一個東西
export default () => { const handleClick = () => {}; return( ) }這種情況下,我們可以用memo第二個參數來拯救對於一次的渲染的局面
// props : { a: 1 , onClick : () => {} } // 在我們知道onClick是做同一個事情的函數的前提下,不比較onClick React.memo(C , (nextProps,prevProps) => nextProps.a === prevProps.a)最後,前後props的onClick,它們只有一種情況是一樣的,把聲明抽象到組件外面去
const handerClick = () => {}; export defalut () => { return () };
這時候,有沒有想起class組件裡面總是onClick={this.handerClick}呢?this.handleClick一直都是同一個函數。這種情況,子組件為函數組件的時候,包一層memo實現PureComponent的效果
useCallback
函數組件把函數定義寫在外面,是可以解決問題。但是,如果handleClick依賴組件內部的一些變量,那handleClick又不得不寫在裡面(當然利用引用類型可以解決)。或者還是正常些,靠memo第二個參數來控制要不要重新渲染子函數組件,但是無論怎樣,都存在一個問題,就是那一塊代碼寫在裡面呢,都無法避免代碼的執行和函數的重新定義,比如
function a (){ const b = function(){ console.log(1); // 很多很多代碼 } } a() a() //函數b又被定義了一次如果我們通過依賴來確定前後兩次是不是同一個函數,我們可以用函數記憶來實現整個功能
let prev let prevDeps function memorize(fn,deps){ if(prev && isEqual(deps,prevDeps)){ return prev } prevDeps = deps; prev = fn; return fn; } function a (){ const b = memorize(function(){ console.log(1); },[]) } a(); a(); // 函數b是同一個類似函數記憶的遠離,後來有了useCallBack的出現,多了一種新的解決方案,根據依賴生成一個函數:
const handeClick = useCallBack(() => { console.log(dep) },[dep])當dep不變,每一次函數組件的執行,handelClick都是同一個函數。如果dep變了,那麼handleClick又是一個新的函數。
export default () => { // 沒有依賴,永遠是同一個函數 const handleClick = useCallBack(() => {},[]); // 依賴a,重新執行函數組件,a不變的,是同一個函數 // a變了handleClick是新的函數 const handelClick1 = useCallBack(() => {},[a]); return() }
react組件也是一個函數,那其實useCallback還可以做一個函數組件:
export default () => { const handleClick = useCallback(() => {},[]); const Cpn = useCallback(({name}) => { return },[handleClick]) return () }
當然這只是一個簡單的場景,如果用了hooks,還沒有解決問題或者暫時沒有想到優雅的封裝技巧,想用高階組件的時候,不妨嘗試一下useCallback
useMemo
const a = useMemo(() => memorizeValue,deps);當deps不變,a的值還是上次的memorizeValue,省去了重新計算的過程。如果memorizeValue是一個函數,和useCallback是一樣的效果
useCallback(fn,deps) <=> useMemo(() => fn ,deps)我們可以試一下同步執行的代碼,當時間非常長的時候,useMemo可以發揮它的作用了:
// 強制更新組件 const useForceUpdate = () => { const forceUpdate = useState(0)[1]; return () => forceUpdate( x => x + 1) }; // 一個很耗時間的代碼 function slowlyAdd(n){ console.time('add slowly'); let res = n; for (let i = 0 ; i < 2000000000 ; i ++){ res += 1; } console.timeEnd('add slowly'); return res; } // useMemo 記憶結果的一個自定義hook function useSlowlyAdd(n){ const res = useMemo(() => { return slowlyAdd(n); },[n]); return res; } export default () => { const [ count , add ] = useState(1); const forceUpdate = useForceUpdate(); const handleClick = useCallback(() => {},[]); useSlowlyAdd(count); // 第一次這裡會耗很多時間,頁面卡死一陣 return( <> <> ) }第一次進來,頁面暫時沒有人設反應一陣,這是因為slowlyAdd佔用了住縣城。當我們點擊‘更新頁面’更新的時候,頁面並沒有卡斯,而且組件也重新渲染執行了一次。當我們點擊+,頁面又開始卡死一陣。
這是因為點擊+的時候,修改了useMemo的依賴n,n變了重新計算,計算耗費時間。如果點擊更新頁面,沒有修改到依賴n,不會重新計算,頁面也不會卡。
當然useMemo也可以做高階組件,用起來的時候,可以寫成reactElement的形式了
const HOC = useMemo(() => ,deps)收藏
舉報
關鍵字: 作用域 組件 React.memo