詳解React中Props的淺對(duì)比
上一周去面試的時(shí)候,面試官我PureComponent里是如何對(duì)比props的,概念已經(jīng)牢記腦中,脫口而出就是淺對(duì)比,接著面試官問(wèn)我是如何淺對(duì)比的,結(jié)果我就沒(méi)回答上來(lái)。
趁著周末,再來(lái)看看源碼里是如何實(shí)現(xiàn)的。
類(lèi)組件的Props對(duì)比
類(lèi)組件是否需要更新需要實(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í)際上并沒(méi)有一個(gè)默認(rèn)的方法。
接下來(lái)看看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í)際上并沒(méi)有單獨(dú)寫(xiě)一個(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)的值是否相同。
原來(lái)原理是這樣簡(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,方便之后的類(lèi)型判斷。
React.memo組件的創(chuàng)建會(huì)稍微復(fù)雜一些,由于可以傳入第二個(gè)自定義的compare函數(shù),所以在內(nèi)部其實(shí)會(huì)被定義為2種類(lèi)型的Fiber節(jié)點(diǎn)。
- 沒(méi)有傳入compare函數(shù)的為SimpleMemoComponent。
- 傳入了自定義compare函數(shù)的為MemoComponent。
但是實(shí)際對(duì)于Props的比較都是相同的,默認(rèn)都是調(diào)用shallowEqual方法來(lái)對(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è)組件,我也沒(méi)大看懂,藍(lán)廋香菇,大概是和更新調(diào)度相關(guān)的。
SimpleMemoComponent的Fiber節(jié)點(diǎn)實(shí)際等于改了個(gè)名的函數(shù)組件,走流程會(huì)直接走到函數(shù)組件里,而MemoComponent則是套了一層殼,需要先把殼剝開(kāi)生成子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?v6實(shí)現(xiàn)權(quán)限管理+自動(dòng)替換頁(yè)面標(biāo)題的案例
這篇文章主要介紹了react-router?v6實(shí)現(xiàn)權(quán)限管理+自動(dòng)替換頁(yè)面標(biāo)題,這次項(xiàng)目是有三種權(quán)限,分別是用戶,商家以及管理員,這次寫(xiě)的權(quán)限管理是高級(jí)權(quán)限能訪問(wèn)低級(jí)權(quán)限的所有頁(yè)面,但是低級(jí)權(quán)限不能訪問(wèn)高級(jí)權(quán)限的頁(yè)面,需要的朋友可以參考下2023-05-05
圖文示例講解useState與useReducer性能區(qū)別
這篇文章主要為大家介紹了useState與useReducer性能區(qū)別圖文示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-05-05
React項(xiàng)目中動(dòng)態(tài)插入HTML內(nèi)容的實(shí)現(xiàn)
本文主要介紹了React項(xiàng)目中動(dòng)態(tài)插入HTML內(nèi)容的實(shí)現(xiàn),通過(guò)使用React的dangerouslySetInnerHTML屬性,我們可以將HTML內(nèi)容插入到組件中,具有一定的參考價(jià)值,感興趣的可以了解一下2023-10-10
基于React-Dropzone開(kāi)發(fā)上傳組件功能(實(shí)例演示)
這篇文章主要介紹了基于React-Dropzone開(kāi)發(fā)上傳組件,主要講述的是在React-Flask框架上開(kāi)發(fā)上傳組件的技巧,需要的朋友可以參考下2021-08-08
react中將html字符串渲染到頁(yè)面的實(shí)現(xiàn)方式
這篇文章主要介紹了react中將html字符串渲染到頁(yè)面的實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-12-12
React 并發(fā)功能體驗(yàn)(前端的并發(fā)模式)
React 是由 Facebook 軟件工程師 Jordan Walke 創(chuàng)建,React 的第一個(gè)版本在七年前問(wèn)世,現(xiàn)在,F(xiàn)acebook 負(fù)責(zé)維護(hù),本文給大家介紹React 并發(fā)功能體驗(yàn)前端并發(fā)模式的問(wèn)題,感興趣的朋友跟隨小編一起看看吧2021-07-07

