詳解React中Props的淺對比
上一周去面試的時候,面試官我PureComponent
裡是如何對比props
的,概念已經牢記腦中,脫口而出就是淺對比,接著面試官問我是如何淺對比的,結果我就沒回答上來。
趁著周末,再來看看源碼裡是如何實現的。
類組件的Props對比
類組件是否需要更新需要實現shouldComponentUpdate方法,通常講的是如果繼承的是PureComponent則會有一個默認淺對比的實現。
// ReactBaseClasses.js function ComponentDummy() {} ComponentDummy.prototype = Component.prototype; /** * Convenience component with default shallow equality check for sCU. */ function PureComponent(props, context, updater) { this.props = props; this.context = context; // If a component has string refs, we will assign a different object later. this.refs = emptyObject; this.updater = updater || ReactNoopUpdateQueue; } const pureComponentPrototype = (PureComponent.prototype = new ComponentDummy()); pureComponentPrototype.constructor = PureComponent; // Avoid an extra prototype jump for these methods. Object.assign(pureComponentPrototype, Component.prototype); pureComponentPrototype.isPureReactComponent = true;
PureComponent
的實現如上,我以前以為在聲明時默認會實現shouldComponentUpdate
方法,但實際上並沒有一個默認的方法。
接下來看看shouldComponentUpdate
方法的調用。
// ReactFiberClassComponent.js function checkShouldComponentUpdate( workInProgress, ctor, oldProps, newProps, oldState, newState, nextContext, ) { const instance = workInProgress.stateNode; // 如果實利實現瞭shouldComponentUpdate則返回調用它的結果 if (typeof instance.shouldComponentUpdate === 'function') { const shouldUpdate = instance.shouldComponentUpdate( newProps, newState, nextContext, ); return shouldUpdate; } // PureReactComponent的時候進行淺對比 if (ctor.prototype && ctor.prototype.isPureReactComponent) { return ( !shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState) ); } return true; }
可以看出實際上並沒有單獨寫一個shouldComponentUpdate方法給PureReactComponent,而是在對比的時候就返回淺對比的結果。
淺對比的答案都在shallowEqual方法裡瞭。
shallowEqual 淺對比
// shallowEqual.js function shallowEqual(objA: mixed, objB: mixed): boolean { // 一樣的對象返回true if (Object.is(objA, objB)) { return true; } // 不是對象或者為null返回false if ( typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null ) { return false; } const keysA = Object.keys(objA); const keysB = Object.keys(objB); // key數量不同返回false if (keysA.length !== keysB.length) { return false; } // 對應key的值不相同返回false for (let i = 0; i < keysA.length; i++) { if ( !hasOwnProperty.call(objB, keysA[i]) || !Object.is(objA[keysA[i]], objB[keysA[i]]) ) { return false; } } return true; }
shallowEqual方法原理很簡單瞭
- 先判斷兩者是否為同一對象。
- 判斷兩者的值是否不為object或為null。
- 對比兩者key的長度。
- 判斷兩者key對應的值是否相同。
原來原理是這樣簡單的對比,如果我面試的時候能夠口噴源碼,會不會工資更高一些呢?
函數組件的淺對比
函數組件的淺對比方式則使用React.memo方法實現。
// ReactMemo.js export function memo<Props>( type: React$ElementType, compare?: (oldProps: Props, newProps: Props) => boolean, ) { const elementType = { $$typeof: REACT_MEMO_TYPE, type, compare: compare === undefined ? null : compare, }; return elementType; }
React.memo方法同樣支持傳入compare函數最為第二個參數。
內部的處理其實是手動創建瞭一個$$typeof為REACT_MEMO_TYPE的ReactElement,方便之後的類型判斷。
React.memo組件的創建會稍微復雜一些,由於可以傳入第二個自定義的compare函數,所以在內部其實會被定義為2種類型的Fiber節點。
- 沒有傳入compare函數的為SimpleMemoComponent。
- 傳入瞭自定義compare函數的為MemoComponent。
但是實際對於Props的比較都是相同的,默認都是調用shallowEqual方法來對比。
updateSimpleMemoComponent
if ( shallowEqual(prevProps, nextProps) && current.ref === workInProgress.ref ) { // ... }
updateMemoComponent
// ... let compare = Component.compare; compare = compare !== null ? compare : shallowEqual; if (compare(prevProps, nextProps) && current.ref === workInProgress.ref) { return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes); } // ...
至於為什麼要分為2個組件,我也沒大看懂,藍廋香菇,大概是和更新調度相關的。
SimpleMemoComponent的Fiber節點實際等於改瞭個名的函數組件,走流程會直接走到函數組件裡,而MemoComponent則是套瞭一層殼,需要先把殼剝開生成子Fiber節點,再由子Fiber節點的判斷走到函數組件裡。
以上就是Props淺對比的分析瞭~
以上就是詳解React中Props的淺對比的詳細內容,更多關於React中Props的淺對比的資料請關註WalkonNet其它相關文章!
推薦閱讀:
- 提高React界面性能的十個技巧
- React重新渲染超詳細講解
- React 性能優化之非必要的渲染問題解決
- React中immutable的UI組件渲染性能詳解
- 基於React Hooks的小型狀態管理詳解