前面
先了解一下在react中的class類組件中的性能優化方面,主要集中於一下兩點
1.調用setState時,就會觸發組件重新渲染,無論前后state是否改變
2.父組件更新,子組件也會自動更新
解決方案:
1.使用 immutable 進行比較,在不相等的時候調用 setState
2.在shouldComponentUpdate 中判斷前后的數據(prop,state)是否一致,如果沒有變化就返回 false 來阻止更新。
3.使用 pureComponent 代替 component,pureComponent 自帶通過 props 和 state 的淺對比來實現 shouldComponentUpdate()
pureComponent 缺點:可能會因深層的數據不一致而產生錯誤的否定判斷,
從而 shouldComponentUpdate 的結果返回 false,界面得不到更新
下面說下 hooks 中的函數式組件如何優化組件性能方案:
在函數式組件中失去了 shouldComponentUpdate ,我發通過判斷前后狀態來決定是否更新。
在函數式組件中,react 不再區分 mount 和 update 兩個狀態,也就是說函數組件的每一次調用都會執行其內部的所有略記,會帶來較大的性能損耗。在此,hooks 中出現了兩個鈎子 useMemo 和 useCallback 來解決函數式組件的性能方案。
先看下 useMemo 和 useCallback 的源碼
function useCallback<T extends (...args: any[]) => any>(callback: T, deps: DependencyList): T; function useMemo<T>(factory: () => T, deps: DependencyList | undefined): T;
參數形式和 useEffect 一致,useEffect 作用是用於處理副作用,這兩個則不行。
useMemo 和 useCallback 都會在組件第一次渲染的時候執行,之后會在依賴改變時再次執行;
都返回緩存的值,useMemo 返回緩存的變量,useCallback 返回緩存的函數。
先說下 useMemo
優點:useMemo包裹的變量,相當於對變量做了緩存,當父組件重新渲染時,變量不會改變==》子組件不會重新渲染
function Test() { const [count, setCount] = useState(1); const [val, setVal] = useState(''); // sum1 每次在 count 與 val 有一個改變的時候都會刷新組件重新執行 sum1。 // 但是這里的計算 sum1 只依賴於 count 的值,在 val 修改的時候沒有必要進行 sum1 的計算。 // 這種情況下,我們就可以使用 useMemo,只在count 的值修改時執行計算(sum2) const sum1 = () => { console.log('sum1111'); let sum = 0; for (let i = 0; i < count; i++) { sum += i; } return sum; }; // 每次在 count 的值改變的時候才重新執行計算 const sum2 = useMemo(() => { console.log('sum2222'); let sum = 0; for (let i = 0; i < count; i++) { sum += i; } return sum; }, [count]); return ( <div> <h1>{count}+++{val}+++{sum1()}+++{sum2()}</h1> <div> <button onClick={() => setCount(count + 1)}>+++</button> <input type='text' value={val} onChange={event => setVal(event.target.value)}/> </div> </div> ); }
下面說下 useCallback
為什么使用 useCallback,以及 useCallback 所能帶來的性能提升
和 useMemo 類似,當回調函數床底給經過優化的並使用引用相等性去避免非必要渲染的子組件時,他非常有用,和 pureComponent 的作用相同。就是說父組件傳遞一個函數給子組件的時候,由於父組件的更新會導致該函數重新生成從而傳遞給子組件的函數引用發生了變化,這就會導致子組件也會更新, 很多情況下子組件的更新並不是必要的,所以通過 useCallback 來緩存傳遞給子組件的函數。
優點:useCallback包裹的函數,相當於對函數做了緩存,當父組件重新渲染時,函數不會重新定義==》子組件不會重新渲染
代碼示例
const Child = ({getNum}) => { return <h1>數據:{getNum}</h1>; }; function Test() { const [count, setCount] = useState(1); const [val, setVal] = useState(''); const sum = useCallback(() => { return Array.from({length: count}, (v, i) => i).reduce((a, b) => a + b); }, [count]); return ( <div> <Child getNum={sum}/> <h1>{count}+++{val}++++{sum()}</h1> <div> <button onClick={() => setCount(count + 1)}>+++</button> <input type='text' value={val} onChange={event => setVal(event.target.value)}/> </div> </div> ); } // 以上只有當 count 改變的時候child組件才會重新渲染
總結:
memo用於包裹子組件;useCallback和useMemo用於父組件向子組件傳值時,即如果是組件內部自己用的函數和變量,不需要使用useCallback和useMemo。