初探memo
首先讓我們用一個例子走進React.memo的世界
呆呆的函數組件 - 沒有使用memo
對於一個函數組件來說,如果沒有使用React.memo就好比是一個人沒有腦子,就笨笨的呆呆的
不信我們就來看下面的Demo
點擊訪問演示Demo
GIF打開UC瀏覽器 查看更多精彩圖片
讓我們來分析下上圖發生的流程:
1. 頁面第一次加載,渲染App組件和B組件,控制檯打印效果如上圖
2. 點擊按鈕,改變App組件內的值。App組件和B組件全都發生更新那麼問題就來了,按正常邏輯來說,應該是這樣的流程纔對:
3.頁面第一次渲染,App組件和B組件分別更新,並打印App組件內的數據發生變化所以,App組件重新渲染改變的數據和B組件毛關係沒有,B組件維持原狀,不進行更新渲染but理想異常豐滿的,現實十分骨幹的。 事實就是不但App組件發生了更新,B組件也跟着進行了更新,這不是我們想要的,因爲對於B組件來說:明明老子啥都沒幹,卻還非要我再重新穿一遍衣服?
無效渲染的原因
那麼造成無效渲染的原因是啥呢?
其實簡單說來是這樣的:函數組件本身沒有識別prop值的能力,每次父組件更新的時候都相當於是給子組件一個新的prop值。所以就相當於B組件這小子因爲沒帶腦子(React.memo),是個呆呆的二傻子,所以他做爲一個普通組件,就沒有分別prop的能力,當他看到別人都更新了也就跟着把自己也造了一遍,因此就會造成上面中的問題。
給憨憨帶上腦子 - 使用memo進行包裹
給函數組件帶上腦子 當我們給一個函數組件帶上腦子的時候,就想下面這樣
就不會發生上面那種,無腦render組件的情況了
試着把上面demo中B組件代碼裏最後一行的註釋放開試一下吧
然後像上面一樣再次點擊一下按鈕,看看控制檯的打印結果:
GIF打開UC瀏覽器 查看更多精彩圖片
Yep! 只更新了App組件,符合預期!
那麼到底是爲什麼造成的這種原因呢
所以到這裏還不算完,讓我們進一步升溫
激情升溫 - 深入探索
到這裏其實我們還是不太清楚memo是怎麼做到避免無效更新的,接下來我們就來扒一扒!
class組件中的性能優化點
不知道大家有沒有發現class組件中也有一個這樣作用的東西,叫做PureComponent,它的功能和memo是一毛一樣的。
來回顧一下,我們在class組件中經常用到的寫法:
總的來說其實PureComponnet和memo都是通過對props值的淺比較來決定該組件是否需要更新的。
如果我們在class組件中,不主動使用PureComponent,也可以手動的去決定該組件是否更新,具體做法:
在生命週期shouldComponentUpdate,來通過對當前porps以及state值的對比,然後返回一個布爾值(true或者false)來決定該組件是否更新。其實PureComponent組件就是把這對比值的部分功能幫我們完成了,方便我們直接使用,而不用再去手動的去寫代碼進行類似的優化。
memo的功能實現
這裏是我的猜想哈,memo的原理和PureComponent應該是一樣的,從開發者的角度去想,既然class組件有這樣一個優化方法,那既然要推行Hook,函數組件也必定需要一個類似功能的方法去幫助大家減少代碼優化的工作量。所以感覺兩者在功能的實現上應該大部分都是一致的。 這裏也放上一段React中PureComponent進行淺比較的代碼,方便大家進一步理解
這就是react中進行淺層比較的源碼,也是PureComponent和memo決定是否更新組件的重要依據。
memo配合useMemo、useCallback
一般在項目的優化實踐中,memo包裹的函數組件都是要配合useMemo和useCallback來使用的
對於useMemo和useCallback其實我不準備長篇大幅的講述了,因爲社區已經有很多不錯的文章了,大家可以搜來看一下。 我這裏只做一個說人話的簡單介紹就好了
useCallback
返回值是一個函數(memoizedCallback),這個函數就是作爲第一個參數傳進去的那個。區別就是作爲返回值的這個函數是一個memoized的版本,用人話理解就是:保持了函數的引用。不會在組件更新時,去重新聲明函數,從而改變在內存中的引用地址。除非是第二個參數數組裏的依賴項發生改變,否則這個做爲返回值的函數(memoizedCallback)就一直保持原先的狀態
應用場景
經常使用在父組件A向子組件B傳遞一個函數作爲prop值的時候
父組件A:
子組件B:
useMemo
其實和useCallback很像,只不過是useMemo返回的是一個值,而不是一個函數useCallback 的第一個參數是函數,這個函數的返回值會作爲useMemo的返回值memoizedValue)除非是第二個參數數組裏的依賴項發生改變,否則這個做爲返回值(memoizedValue)就一直保持原先的值
useCallback能做的事useMemo都能做,但是還是推薦各司其職