詳解React中Props的淺對(duì)比
上一周去面試的時(shí)候,面試官我PureComponent
里是如何對(duì)比props
的,概念已經(jīng)牢記腦中,脫口而出就是淺對(duì)比,接著面試官問我是如何淺對(duì)比的,結(jié)果我就沒回答上來。
趁著周末,再來看看源碼里是如何實(shí)現(xiàn)的。
類組件的Props對(duì)比
類組件是否需要更新需要實(shí)現(xiàn)shouldComponentUpdate方法,通常講的是如果繼承的是PureComponent則會(huì)有一個(gè)默認(rèn)淺對(duì)比的實(shí)現(xiàn)。
// 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
的實(shí)現(xiàn)如上,我以前以為在聲明時(shí)默認(rèn)會(huì)實(shí)現(xiàn)shouldComponentUpdate
方法,但實(shí)際上并沒有一個(gè)默認(rèn)的方法。
接下來看看shouldComponentUpdate
方法的調(diào)用。
// ReactFiberClassComponent.js function checkShouldComponentUpdate( workInProgress, ctor, oldProps, newProps, oldState, newState, nextContext, ) { const instance = workInProgress.stateNode; // 如果實(shí)利實(shí)現(xiàn)了shouldComponentUpdate則返回調(diào)用它的結(jié)果 if (typeof instance.shouldComponentUpdate === 'function') { const shouldUpdate = instance.shouldComponentUpdate( newProps, newState, nextContext, ); return shouldUpdate; } // PureReactComponent的時(shí)候進(jìn)行淺對(duì)比 if (ctor.prototype && ctor.prototype.isPureReactComponent) { return ( !shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState) ); } return true; }
可以看出實(shí)際上并沒有單獨(dú)寫一個(gè)shouldComponentUpdate方法給PureReactComponent,而是在對(duì)比的時(shí)候就返回淺對(duì)比的結(jié)果。
淺對(duì)比的答案都在shallowEqual方法里了。
shallowEqual 淺對(duì)比
// shallowEqual.js function shallowEqual(objA: mixed, objB: mixed): boolean { // 一樣的對(duì)象返回true if (Object.is(objA, objB)) { return true; } // 不是對(duì)象或者為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數(shù)量不同返回false if (keysA.length !== keysB.length) { return false; } // 對(duì)應(yīng)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方法原理很簡(jiǎn)單了
- 先判斷兩者是否為同一對(duì)象。
- 判斷兩者的值是否不為object或?yàn)閚ull。
- 對(duì)比兩者key的長(zhǎng)度。
- 判斷兩者key對(duì)應(yīng)的值是否相同。
原來原理是這樣簡(jiǎn)單的對(duì)比,如果我面試的時(shí)候能夠口噴源碼,會(huì)不會(huì)工資更高一些呢?
函數(shù)組件的淺對(duì)比
函數(shù)組件的淺對(duì)比方式則使用React.memo方法實(shí)現(xiàn)。
// 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函數(shù)最為第二個(gè)參數(shù)。
內(nèi)部的處理其實(shí)是手動(dòng)創(chuàng)建了一個(gè)$$typeof為REACT_MEMO_TYPE的ReactElement,方便之后的類型判斷。
React.memo組件的創(chuàng)建會(huì)稍微復(fù)雜一些,由于可以傳入第二個(gè)自定義的compare函數(shù),所以在內(nèi)部其實(shí)會(huì)被定義為2種類型的Fiber節(jié)點(diǎn)。
- 沒有傳入compare函數(shù)的為SimpleMemoComponent。
- 傳入了自定義compare函數(shù)的為MemoComponent。
但是實(shí)際對(duì)于Props的比較都是相同的,默認(rèn)都是調(diào)用shallowEqual方法來對(duì)比。
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個(gè)組件,我也沒大看懂,藍(lán)廋香菇,大概是和更新調(diào)度相關(guān)的。
SimpleMemoComponent的Fiber節(jié)點(diǎn)實(shí)際等于改了個(gè)名的函數(shù)組件,走流程會(huì)直接走到函數(shù)組件里,而MemoComponent則是套了一層殼,需要先把殼剝開生成子Fiber節(jié)點(diǎn),再由子Fiber節(jié)點(diǎn)的判斷走到函數(shù)組件里。
以上就是Props淺對(duì)比的分析了~
以上就是詳解React中Props的淺對(duì)比的詳細(xì)內(nèi)容,更多關(guān)于React中Props的淺對(duì)比的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
React Router 5.1.0使用useHistory做頁面跳轉(zhuǎn)導(dǎo)航的實(shí)現(xiàn)
本文主要介紹了React Router 5.1.0使用useHistory做頁面跳轉(zhuǎn)導(dǎo)航的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-11-11React-Hook中使用useEffect清除定時(shí)器的實(shí)現(xiàn)方法
這篇文章主要介紹了React-Hook中useEffect詳解(使用useEffect清除定時(shí)器),主要介紹了useEffect的功能以及使用方法,還有如何使用他清除定時(shí)器,需要的朋友可以參考下2022-11-11React路由組件傳參的三種方式(params、search、state)
本文主要介紹了React路由組件傳參的三種方式,主要包括了params、search、state,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07React中如何設(shè)置多個(gè)className
這篇文章主要介紹了React中如何設(shè)置多個(gè)className問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-01-01react.js使用webpack搭配環(huán)境的入門教程
本文主要介紹了react 使用webpack搭配環(huán)境的入門教程,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-08-08解決React報(bào)錯(cuò)Property does not exist on 
這篇文章主要為大家介紹了React報(bào)錯(cuò)Property does not exist on type 'JSX.IntrinsicElements'解決方法,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12react 移動(dòng)端實(shí)現(xiàn)列表左滑刪除的示例代碼
這篇文章主要介紹了react 移動(dòng)端實(shí)現(xiàn)列表左滑刪除的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-07-07React版本18.xx降低為17.xx的方法實(shí)現(xiàn)
由于現(xiàn)在react默認(rèn)創(chuàng)建是18.xx版本,但是我們現(xiàn)在大多使用的還是17.xx或者更低的版本,于是要對(duì)react版本進(jìn)行降級(jí),本文主要介紹了React版本18.xx降低為17.xx的方法實(shí)現(xiàn),感興趣的可以了解一下2023-11-11