react組件的創(chuàng)建與更新實現(xiàn)流程詳解_第1頁
react組件的創(chuàng)建與更新實現(xiàn)流程詳解_第2頁
react組件的創(chuàng)建與更新實現(xiàn)流程詳解_第3頁
react組件的創(chuàng)建與更新實現(xiàn)流程詳解_第4頁
react組件的創(chuàng)建與更新實現(xiàn)流程詳解_第5頁
已閱讀5頁,還剩11頁未讀 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認(rèn)領(lǐng)

文檔簡介

第react組件的創(chuàng)建與更新實現(xiàn)流程詳解目錄React源碼執(zhí)行流程圖legacyRenderSubtreeIntoContainerlegacyCreateRootFromDOMContainercreateLegacyRootReactDOMBlockingRootcreateRootImplcreateContainercreateFiberRootcreateHostRootFibercreateFiberupdateContainer總結(jié)這一章節(jié)就來講講ReactDOM.render()方法的內(nèi)部實現(xiàn)與流程吧。

因為初始化的源碼文件部分所涵蓋的內(nèi)容很多,包括創(chuàng)建渲染、更新渲染、Fiber樹的創(chuàng)建與diff,element的創(chuàng)建與插入,還包括一些優(yōu)化算法,所以我就整個的React執(zhí)行流程畫了一個簡單的示意圖。

React源碼執(zhí)行流程圖

從圖中我們很清晰的看到ReactDOM.render()之后我們的組件具體干了什么事情,那么我們進入源碼文件一探究竟吧。

//packages/react-dom/src/client/ReactDOMLegacy.js

exportfunctionrender(

element:React$Elementany,//經(jīng)過babel解析后的element

container:Container,//根組件節(jié)點:document.getElementById('root')..

callback:Function,//回調(diào)

//做合法容器的驗證(根組件)

invariant(

isValidContainer(container),

'TargetcontainerisnotaDOMelement.',

//開發(fā)模式下

if(__DEV__){

constisModernRoot=

isContainerMarkedAsRoot(container)

container._reactRootContainer===undefined;

if(isModernRoot){

console.error(

'YouarecallingReactDOM.render()onacontainerthatwaspreviously'+

'passedtoReactDOM.createRoot().Thisisnotsupported.'+

'Didyoumeantocallroot.render(element)',

//返回legacyRenderSubtreeIntoContainer

returnlegacyRenderSubtreeIntoContainer(

null,

element,

container,

false,

callback,

所以當(dāng)前render函數(shù)僅僅只是做了部分邏輯,閱讀React源碼,給你一個直觀的感受就是他拆分的顆粒度非常的細(xì),很多重復(fù)命名的函數(shù),可能是見名知意的變量名只有那么幾個常見的組合吧,這也是React作者的用心良苦吧。

追根究底我們還是得看一看legacyRenderSubtreeIntoContainer究竟干了些不為人知的事情呢

legacyRenderSubtreeIntoContainer

functionlegacyRenderSubtreeIntoContainer(

parentComponent:React$Componentany,any,//父級組件

children:ReactNodeList,//當(dāng)前元素

container:Container,//容器eg:getElementById('root')

forceHydrate:boolean,callback:Function,

if(__DEV__){

topLevelUpdateWarnings(container);

warnOnInvalidCallback(callback===undefinednull:callback,'render');

//TODO:Without`any`type,Flowsays"Propertycannotbeaccessedonany

//memberofintersectiontype."Whyyyyyy.

letroot:RootType=(container._reactRootContainer:any);

letfiberRoot;

//如果有根組件,表示不是初始化渲染,則走下面的批量更新

//沒有根組件,那么就要去創(chuàng)建根組件了

if(!root){

//初始化掛載

root=container._reactRootContainer=legacyCreateRootFromDOMContainer(

container,

forceHydrate,

fiberRoot=root._internalRoot;

if(typeofcallback==='function'){

constoriginalCallback=callback;

callback=function(){

constinstance=getPublicRootInstance(fiberRoot);

originalCallback.call(instance);

//不必要的批量更新

unbatchedUpdates(()={

updateContainer(children,fiberRoot,parentComponent,callback);

}else{

fiberRoot=root._internalRoot;

if(typeofcallback==='function'){

constoriginalCallback=callback;

callback=function(){

constinstance=getPublicRootInstance(fiberRoot);

originalCallback.call(instance);

//批量更新

updateContainer(children,fiberRoot,parentComponent,callback);

returngetPublicRootInstance(fiberRoot);

}

有根節(jié)點的情況下,我們判定為非首次渲染狀態(tài),執(zhí)行updateContainer沒有根節(jié)點的情況下,我們判定為首次渲染,接著去創(chuàng)建根節(jié)點,執(zhí)行l(wèi)egacyCreateRootFromDOMContainer,拿到了root之后,我們會去觸發(fā)執(zhí)行updateContainer

legacyCreateRootFromDOMContainer

functionlegacyCreateRootFromDOMContainer(

container:Container,//容器

forceHydrate:boolean,//value:false

):RootType{

constshouldHydrate=

forceHydrate||shouldHydrateDueToLegacyHeuristic(container);

//Firstclearanyexistingcontent.

if(!shouldHydrate){

letwarned=false;

letrootSibling;

while((rootSibling=container.lastChild)){

if(__DEV__){

if(

!warned

rootSibling.nodeType===ELEMENT_NODE

(rootSibling:any).hasAttribute(ROOT_ATTRIBUTE_NAME)

warned=true;

console.error(

'render():TargetnodehasmarkuprenderedbyReact,butthere'+

'areunrelatednodesaswell.Thisismostcommonlycausedby'+

'white-spaceinsertedaroundserver-renderedmarkup.',

container.removeChild(rootSibling);

if(__DEV__){

if(shouldHydrate!forceHydrate!warnedAboutHydrateAPI){

warnedAboutHydrateAPI=true;

console.warn(

'render():CallingReactDOM.render()tohydrateserver-renderedmarkup'+

'willstopworkinginReactv18.ReplacetheReactDOM.render()call'+

'withReactDOM.hydrate()ifyouwantReacttoattachtotheserverHTML.',

//關(guān)注createLegacyRoot

returncreateLegacyRoot(

container,

shouldHydrate

hydrate:true,

:undefined,

}

createLegacyRoot

exportfunctioncreateLegacyRoot(

container:Container,//容器

options:RootOptions,

):RootType{

//關(guān)注ReactDOMBlockingRoot

returnnewReactDOMBlockingRoot(container,LegacyRoot,options);

}

ReactDOMBlockingRoot

functionReactDOMBlockingRoot(

container:Container,//容器

tag:RootTag,//LegacyRoot=0;BlockingRoot=1;ConcurrentRoot=2;

options:void|RootOptions,

this._internalRoot=createRootImpl(container,tag,options);

}

我們在這里看到this._internalRoot出來了,因為在先前這個值會給到fiberRoot,所以我們再去看一看這個_internalRoot是怎么創(chuàng)建出來的相關(guān)參考視頻講解:進入學(xué)習(xí)

createRootImpl

functioncreateRootImpl(

container:Container,tag:RootTag,options:void|RootOptions,

//TagiseitherLegacyRootorConcurrentRoot

consthydrate=options!=nulloptions.hydrate===true;

consthydrationCallbacks=

(options!=nulloptions.hydrationOptions)||null;

constmutableSources=

(options!=null

options.hydrationOptions!=null

options.hydrationOptions.mutableSources)||

null;

//關(guān)注createContainer

constroot=createContainer(container,tag,hydrate,hydrationCallbacks);

markContainerAsRoot(root.current,container);

constcontainerNodeType=container.nodeType;

if(enableEagerRootListeners){

constrootContainerElement=

container.nodeType===COMMENT_NODEcontainer.parentNode:container;

listenToAllSupportedEvents(rootContainerElement);

}else{

if(hydratetag!==LegacyRoot){

constdoc=

containerNodeType===DOCUMENT_NODE

container

:container.ownerDocument;

//WeneedtocastthisbecauseFlowdoesn'twork

//withthehoistedcontainerNodeType.Ifweinline

//it,thenFlowdoesn'tcomplain.Weintentionally

//hoistittoreducecode-size.

eagerlyTrapReplayableEvents(container,((doc:any):Document));

}elseif(

containerNodeType!==DOCUMENT_FRAGMENT_NODE

containerNodeType!==DOCUMENT_NODE

ensureListeningTo(container,'onMouseEnter',null);

if(mutableSources){

for(leti=0;imutableSources.length;i++){

constmutableSource=mutableSources[i];

registerMutableSourceForHydration(root,mutableSource);

//關(guān)注root

returnroot;

}

見名知意關(guān)注createContainer為創(chuàng)建容器,看其源碼

createContainer

//packages/react-reconciler/src/ReactFiberReconciler.old.js

exportfunctioncreateContainer(

containerInfo:Container,//容器

tag:RootTag,//LegacyRoot=0;BlockingRoot=1;ConcurrentRoot=2;

hydrate:boolean,hydrationCallbacks:null|SuspenseHydrationCallbacks,

):OpaqueRoot{

//關(guān)注createFiberRoot

returncreateFiberRoot(containerInfo,tag,hydrate,hydrationCallbacks);

}

createFiberRoot

exportfunctioncreateFiberRoot(

containerInfo:any,tag:RootTag,hydrate:boolean,hydrationCallbacks:null|SuspenseHydrationCallbacks,

):FiberRoot{

constroot:FiberRoot=(newFiberRootNode(containerInfo,tag,hydrate):any);

if(enableSuspenseCallback){

root.hydrationCallbacks=hydrationCallbacks;

//關(guān)注createHostRootFiber

constuninitializedFiber=createHostRootFiber(tag);

root.current=uninitializedFiber;

uninitializedFiber.stateNode=root;

//初始化更新隊列

initializeUpdateQueue(uninitializedFiber);

returnroot;

}

關(guān)注root.current、uninitializedFiber.stateNode這兩個玩意兒,后面有大作用,我們還是看看createHostRootFiber吧

createHostRootFiber

exportfunctioncreateHostRootFiber(tag:RootTag):Fiber{

letmode;

if(tag===ConcurrentRoot){

mode=ConcurrentMode|BlockingMode|StrictMode;

}elseif(tag===BlockingRoot){

mode=BlockingMode|StrictMode;

}else{

mode=NoMode;

if(enableProfilerTimerisDevToolsPresent){

//AlwayscollectprofiletimingswhenDevToolsarepresent.

//ThisenablesDevToolstostartcapturingtimingatanypoint–

//Withoutsomenodesinthetreehavingemptybasetimes.

mode|=ProfileMode;

returncreateFiber(HostRoot,null,null,mode);

}

一眼望去這里便是對tag的處理,到了后面便是去創(chuàng)建fiber節(jié)點

createFiber

constcreateFiber=function(

tag:WorkTag,pendingProps:mixed,key:null|string,mode:TypeOfMode,

):Fiber{

//$FlowFixMe:theshapesareexactherebutFlowdoesn'tlikeconstructors

returnnewFiberNode(tag,pendingProps,key,mode);

};

那么主角出來了,就是我們的FiberNode,這里才走完初始化的創(chuàng)建流程,

所以大致的流程就是上面的圖里畫的那樣子,創(chuàng)建流程我們就告一段落,那我們再去看看更新的流程是怎么玩的。

我們知道除了ReactDOM.render()會觸發(fā)更新流程之外,我們還有setState、強制更新、hooks里面的setXxxx等等手段可以觸發(fā)更新,所謂setState那么不正好是我們Component原型上掛的方法嘛。我們回顧一下Component,那些更新都是調(diào)用了updater觸發(fā)器上的方法,那么我們?nèi)タ匆幌逻@個東西。

constclassComponentUpdater={

isMounted,

//setState

enqueueSetState(inst,payload,callback){

constfiber=getInstance(inst);

consteventTime=requestEventTime();//獲取更新觸發(fā)的時間

constlane=requestUpdateLane(fiber);//獲取任務(wù)優(yōu)先級

//根據(jù)更新觸發(fā)時間+更新優(yōu)先級來創(chuàng)建更新任務(wù)對象

constupdate=createUpdate(eventTime,lane);//創(chuàng)建更新任務(wù)對象

//constupdate:Update*={

//eventTime,//更新時間

//lane,//優(yōu)先級

//tag:UpdateState,//更新類型:0更新,1替換。,2強制替換,3捕獲型更新

//payload:null,//需要更新的內(nèi)容

//callback:null,//更新完后的回調(diào)

//next:null,//指向下一個更新

//};

//把內(nèi)容填上

update.payload=payload;

if(callback!==undefinedcallback!==null){

if(__DEV__){

//開發(fā)環(huán)境下腰給個警告

warnOnInvalidCallback(callback,'setState');

//如果有回調(diào),那么加上回調(diào)

update.callback=callback;

//constupdate:Update*={

//eventTime,//更新時間you

//lane,//優(yōu)先級you

//tag:UpdateState,//更新類型:0更新,1替換。,2強制替換,3捕獲型更新

//payload:null,//需要更新的內(nèi)容you

//callback:null,//更新完后的回調(diào)you

//next:null,//指向下一個更新

//};

enqueueUpdate(fiber,update);//推入更新隊列

scheduleUpdateOnFiber(fiber,lane,eventTime);//調(diào)度

if(__DEV__){

if(enableDebugTracing){

if(fiber.modeDebugTracingMode){

constname=getComponentName(fiber.type)||'Unknown';

logStateUpdateScheduled(name,lane,payload);

if(enableSchedulingProfiler){

markStateUpdateScheduled(fiber,lane);

//replaceState

enqueueReplaceState(inst,payload,callback){

constfiber=getInstance(inst);

consteventTime=requestEventTime();

constlane=requestUpdateLane(fiber);

constupdate=createUpdate(eventTime,lane);

update.tag=ReplaceState;

update.payload=payload;

if(callback!==undefinedcallback!==null){

if(__DEV__){

warnOnInvalidCallback(callback,'replaceState');

update.callback=callback;

enqueueUpdate(fiber,update);

scheduleUpdateOnFiber(fiber,lane,eventTime);

if(__DEV__){

if(enableDebugTracing){

if(fiber.modeDebugTracingMode){

constname=getComponentName(fiber.type)||'Unknown';

logStateUpdateScheduled(name,lane,payload);

if(enableSchedulingProfiler){

markStateUpdateScheduled(fiber,lane);

//forceUpdate

enqueueForceUpdate(inst,callback){

constfiber=getInstance(inst);

consteventTime=requestEventTime();

constlane=requestUpdateLane(fiber);

constupdate=createUpdate(eventTime,lane);

update.tag=ForceUpdate;

if(callback!==undefinedcallback!==null){

if(__DEV__){

warnOnInvalidCallback(callback,'forceUpdate');

update.callback=callback;

enqueueUpdate(fiber,update);

scheduleUpdateOnFiber(fiber,lane,eventTime);

if(__DEV__){

if(enableDebugTracing){

if(fiber.modeDebugTracingMode){

constname=getComponentName(fiber.type)||'Unknown';

logForceUpdateScheduled(name,lane);

if(enableSchedulingProfiler){

markForceUpdateScheduled(fiber,lane);

};

updateContainer

exportfunctionupdateContainer(

element:ReactNodeList,container:OpaqueRoot,parentComponent:React$Componentany,any,callback:Function,

):Lane{

if(__DEV__){

onScheduleRoot(container,element);

constcurrent=container.current;

consteventTime=requestEventTime();

if(__DEV__){

//$FlowExpectedError-jestisn'taglobal,andisn'trecognizedoutsideoftests

if('undefined'!==typeofjest){

warnIfUnmockedScheduler(current);

warnIfNotScopedWithMatchingAct(current);

constlane=requestUpdateLane(current);

if(enableSchedulingProfiler){

markRenderScheduled(lane);

constcontext=getContextForSubtree(parentComponent);

if(container.context===null){

container.context=context;

}else{

container.pendingContext=context;

if(__DEV__){

if(

ReactCurrentFiberIsRendering

ReactCurrentFiber

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論