ReactDOM.render在react源碼中執(zhí)行原理
ReactDOM.render
通常是如下圖使用,在提供的 container 里渲染一個(gè) React 元素,并返回對(duì)該組件的引用(或者針對(duì)無(wú)狀態(tài)組件返回 null)。
本文主要是將ReactDOM.render的執(zhí)行流程在后續(xù)文章中會(huì)對(duì)創(chuàng)建更新的細(xì)節(jié)進(jìn)行分析,文中的源代碼部分為了方便閱讀將__DEV__
部分的代碼移除掉了。
ReactDOM.render( <App />, document.getElementById('root') );
render
位于:react-dom/src/client/ReactDOMLegacy.js
export function render( element: React$Element<any>, container: Container, callback: ?Function, ) { // 驗(yàn)證container是否為有效的DOM節(jié)點(diǎn) invariant( isValidContainer(container), 'Target container is not a DOM element.', ); return legacyRenderSubtreeIntoContainer( null, element, container, false, callback, ); }
返回了一個(gè)legacyRenderSubtreeIntoContainer函數(shù),這里注意有5個(gè)參數(shù)
parentComponent
: 父組件因?yàn)槭浅醮蝿?chuàng)建所以為null。
children
: 傳入的ReactElement
container
: 渲染React的DOM容器
forceHydrate
: 判斷是否需要協(xié)調(diào),在服務(wù)端渲染的情況下已渲染的DOM結(jié)構(gòu)是類(lèi)似的因此可以在對(duì)比后進(jìn)行復(fù)用。在服務(wù)端渲染的情況下使用ReactDOM.hydrate()與 render() 相同只是forceHydrate會(huì)標(biāo)記為true。
callback
: 渲染完成后的回調(diào)函數(shù)
legacyRenderSubtreeIntoContainer
位于:react-dom/src/client/ReactDOMLegacy.js
作用:
- 判斷是否為初次渲染,如果是就創(chuàng)建root并將root._internalRoot賦值給fiberRoot同時(shí)封裝callback回調(diào),然后調(diào)用unbatchedUpdates立即更新子節(jié)點(diǎn)。
- 如果不是第一次渲染則進(jìn)入正常的updateContainer流程。
- 最后getPublicRootInstance(fiberRoot)返回公開(kāi)的 Root 實(shí)例對(duì)象。
function legacyRenderSubtreeIntoContainer( parentComponent: ?React$Component<any, any>, children: ReactNodeList, container: Container, forceHydrate: boolean, callback: ?Function, ) { // TODO: Without `any` type, Flow says "Property cannot be accessed on any // member of intersection type." Whyyyyyy. let root: RootType = (container._reactRootContainer: any); let fiberRoot; if (!root) { // Initial mount 初次渲染創(chuàng)建FiberRoot root = container._reactRootContainer = legacyCreateRootFromDOMContainer( container, forceHydrate, ); fiberRoot = root._internalRoot; if (typeof callback === 'function') { const originalCallback = callback; callback = function() { const instance = getPublicRootInstance(fiberRoot); originalCallback.call(instance); }; } // Initial mount should not be batched. unbatchedUpdates(() => { updateContainer(children, fiberRoot, parentComponent, callback); }); } else { fiberRoot = root._internalRoot; if (typeof callback === 'function') { const originalCallback = callback; callback = function() { const instance = getPublicRootInstance(fiberRoot); originalCallback.call(instance); }; } // Update updateContainer(children, fiberRoot, parentComponent, callback); } return getPublicRootInstance(fiberRoot); }
legacyCreateRootFromDOMContainer
位于:react-dom/src/client/ReactDOMLegacy.js
初次渲染進(jìn)入創(chuàng)建root的環(huán)節(jié):root = container._reactRootContainer = legacyCreateRootFromDOMContainer(container, forceHydrate)作用:主要是判斷是否為服務(wù)端渲染,如果是的話(huà)就會(huì)復(fù)用存在的dom節(jié)點(diǎn)進(jìn)行協(xié)調(diào)(reconciliation)提高性能,如果不是則會(huì)清空container中的子元素,最后傳入container和shouldHydrate返回createLegacyRoot函數(shù)。
function legacyCreateRootFromDOMContainer( container: Container, forceHydrate: boolean, ): RootType { const shouldHydrate = forceHydrate || shouldHydrateDueToLegacyHeuristic(container); // 判斷是否是服務(wù)端渲染 // First clear any existing content. if (!shouldHydrate) { let warned = false; let rootSibling; while ((rootSibling = container.lastChild)) { container.removeChild(rootSibling); } } return createLegacyRoot( container, shouldHydrate ? { hydrate: true, } : undefined, ); }
createLegacyRoot
位于:react-dom/src/client/ReactDOMRoot.js
作用:返回了一個(gè)ReactDOMBlockingRoot實(shí)例,這里傳入了LegacyRoot是一個(gè)常量=0代表著現(xiàn)在使用的同步渲染模式,是為了后續(xù)的Concurrent可中斷渲染模式做準(zhǔn)備。
export function createLegacyRoot( container: Container, options?: RootOptions, // hydrate ): RootType { return new ReactDOMBlockingRoot(container, LegacyRoot, options); }
ReactDOMBlockingRoot
位于:react-dom/src/client/ReactDOMRoot.js
作用:將createRootImpl
函數(shù)的返回(FiberRoot)掛載到實(shí)例的_internalRoot上
function ReactDOMBlockingRoot( container: Container, tag: RootTag, options: void | RootOptions, ) { this._internalRoot = createRootImpl(container, tag, options); }
createRootImpl
位于:react-dom/src/client/ReactDOMRoot.js
作用:執(zhí)行createContainer拿到FiberRootNode并賦值給root,再通過(guò)markContainerAsRoot將RootFiber掛載到container上。
function createRootImpl( container: Container, tag: RootTag, options: void | RootOptions, ) { // Tag is either LegacyRoot or Concurrent Root const hydrate = options != null && options.hydrate === true; const hydrationCallbacks = (options != null && options.hydrationOptions) || null; // 拿到FiberRootNode const root = createContainer(container, tag, hydrate, hydrationCallbacks); // 將FiberRootNode掛載到container markContainerAsRoot(root.current, container); if (hydrate && tag !== LegacyRoot) { const doc = container.nodeType === DOCUMENT_NODE ? container : container.ownerDocument; eagerlyTrapReplayableEvents(container, doc); } return root; }
createContainer
位于:react-reconciler/src/ReactFiberReconciler.old.js
作用:返回createFiberRoot
export function createContainer( containerInfo: Container, tag: RootTag, hydrate: boolean, hydrationCallbacks: null | SuspenseHydrationCallbacks,): OpaqueRoot { return createFiberRoot(containerInfo, tag, hydrate, hydrationCallbacks); }
createFiberRoot
位于:react-reconciler/src/react-reconciler/src/ReactFiberReconciler.old.js
作用: 新建FiberRoot對(duì)象并賦值給root,初始化Fiber(通常叫做RootFiber)通過(guò)root.current = uninitializedFiber和uninitializedFiber.stateNode = root將兩者聯(lián)系起來(lái)。
執(zhí)行initializeUpdateQueue(uninitializedFiber)
創(chuàng)建一個(gè)更新隊(duì)列,掛載fiber.updateQueue下面
最后將root返回
export function createFiberRoot( containerInfo: any, tag: RootTag, hydrate: boolean, hydrationCallbacks: null | SuspenseHydrationCallbacks,): FiberRoot { // 新建fiberRoot對(duì)象 const root: FiberRoot = (new FiberRootNode(containerInfo, tag, hydrate): any); if (enableSuspenseCallback) { root.hydrationCallbacks = hydrationCallbacks; } // Cyclic construction. This cheats the type system right now because // stateNode is any. //初始化RootFiber const uninitializedFiber = createHostRootFiber(tag); root.current = uninitializedFiber; // RootFiber的stateNode指向FiberRoot uninitializedFiber.stateNode = root; initializeUpdateQueue(uninitializedFiber); return root; }
FiberRoot RootFiber 和 updateQueue
ReactDOM.render主要?jiǎng)?chuàng)建了三個(gè)對(duì)象FiberRooat、RootFiber和Updatequeue下面我們這對(duì)這三個(gè)對(duì)象進(jìn)行分析
FiberRoot
FiberRoot是FiberRootNode(containerInfo, tag, hydrate)的實(shí)例
位于:react-reconciler/src/ReactFiberRoot/FiberRootNode
作用:
- 整個(gè)應(yīng)用的起點(diǎn)
- 包含應(yīng)用掛載的目標(biāo)節(jié)點(diǎn)
- 記錄整個(gè)應(yīng)用更新過(guò)程的各種信息
function FiberRootNode(containerInfo, tag, hydrate) { // 標(biāo)記不同的組件類(lèi)型 this.tag = tag; // 當(dāng)前應(yīng)用對(duì)應(yīng)的Fiber對(duì)象,是Root Fiber // current:Fiber對(duì)象 對(duì)應(yīng)的是 root 節(jié)點(diǎn),即整個(gè)應(yīng)用根對(duì)象 this.current = null; // root節(jié)點(diǎn),render方法接收的第二個(gè)參數(shù) this.containerInfo = containerInfo; // 只有在持久更新中會(huì)用到,也就是不支持增量更新的平臺(tái),react-dom不會(huì)用到 this.pendingChildren = null; this.pingCache = null; //任務(wù)有三種,優(yōu)先級(jí)有高低: //(1)沒(méi)有提交的任務(wù) //(2)沒(méi)有提交的被掛起的任務(wù) //(3)沒(méi)有提交的可能被掛起的任務(wù) //當(dāng)前更新對(duì)應(yīng)的過(guò)期時(shí)間 this.finishedExpirationTime = NoWork; //已經(jīng)完成任務(wù)的FiberRoot對(duì)象,如果你只有一個(gè)Root,那么該對(duì)象就是這個(gè)Root對(duì)應(yīng)的Fiber或null //在commit(提交)階段只會(huì)處理該值對(duì)應(yīng)的任務(wù) this.finishedWork = null; // 在任務(wù)被掛起的時(shí)候通過(guò)setTimeout設(shè)置的返回內(nèi)容,用來(lái)下一次如果有新的任務(wù)掛起時(shí)清理還沒(méi)觸發(fā)的timeout(例如suspense返回的promise) this.timeoutHandle = noTimeout; // 頂層context對(duì)象,只有主動(dòng)調(diào)用renderSubTreeIntoContainer時(shí)才會(huì)被調(diào)用 this.context = null; this.pendingContext = null; // 第一次渲染是否需要調(diào)和 this.hydrate = hydrate; // Node returned by Scheduler.scheduleCallback this.callbackNode = null; this.callbackPriority = NoPriority; //存在root中,最舊的掛起時(shí)間 //不確定是否掛起的狀態(tài)(所有任務(wù)一開(kāi)始均是該狀態(tài)) this.firstPendingTime = NoWork; this.firstSuspendedTime = NoWork; this.lastSuspendedTime = NoWork; this.nextKnownPendingLevel = NoWork; //存在root中,最新的掛起時(shí)間 //不確定是否掛起的狀態(tài)(所有任務(wù)一開(kāi)始均是該狀態(tài)) this.lastPingedTime = NoWork; this.lastExpiredTime = NoWork; this.mutableSourcePendingUpdateTime = NoWork; if (enableSchedulerTracing) { this.interactionThreadID = unstable_getThreadID(); this.memoizedInteractions = new Set(); this.pendingInteractionMap = new Map(); } if (enableSuspenseCallback) { this.hydrationCallbacks = null; } }
RootFiber
RootFiber初始化于const uninitializedFiber = createHostRootFiber(tag) 通過(guò) createFiber 返回 FiberNode的實(shí)例 作用:
- 每個(gè)ReactElement對(duì)應(yīng)一個(gè)Fiber對(duì)象
- 記錄節(jié)點(diǎn)的各種狀態(tài)(方便了hooks,因?yàn)橛涗泂tate和props都是在Fiber只是完成后再掛載到this的例如:pendingProps pendingState memoizedProps memoizedState)
- 串聯(lián)整個(gè)應(yīng)用形成樹(shù)結(jié)構(gòu)
// 位于 react-reconciler/src/ReactFiber.js export function createHostRootFiber(tag: RootTag): Fiber { let mode; if (tag === ConcurrentRoot) { mode = ConcurrentMode | BlockingMode | StrictMode; } else if (tag === BlockingRoot) { mode = BlockingMode | StrictMode; } else { mode = NoMode; } return createFiber(HostRoot, null, null, mode); } const createFiber = function( tag: WorkTag, pendingProps: mixed, key: null | string, mode: TypeOfMode, ): Fiber { return new FiberNode(tag, pendingProps, key, mode); }; // FiberNode結(jié)構(gòu) function FiberNode( tag: WorkTag, pendingProps: mixed, key: null | string, mode: TypeOfMode, ) { // Instance // 標(biāo)記不同的組件類(lèi)型 this.tag = tag; // ReactElement里面的key this.key = key; // ReactElement.type,也就是我們調(diào)用`createElement`的第一個(gè)參數(shù) this.elementType = null; // 異步組件lazy component resolved之后返回的內(nèi)容,一般是`function`或者`class`組件 this.type = null; // 對(duì)應(yīng)節(jié)點(diǎn)的實(shí)例,比如類(lèi)組件就是class的實(shí)例,如果是dom組件就是dom實(shí)例,如果是function component就沒(méi)有實(shí)例這里為空 this.stateNode = null; // Fiber Fiber是個(gè)鏈表通過(guò)child和Sibling連接,遍歷的時(shí)候先遍歷child如果沒(méi)有子元素了則訪問(wèn)return回到上級(jí)查詢(xún)是否有sibling // 指向他在Fiber節(jié)點(diǎn)樹(shù)中的‘parent',用來(lái)在處理完這個(gè)節(jié)點(diǎn)之后向上返回 this.return = null; // 指向第一個(gè)子節(jié)點(diǎn) this.child = null; // 指向自己的兄弟節(jié)點(diǎn),兄弟節(jié)點(diǎn)的return指向同一個(gè)副節(jié)點(diǎn) this.sibling = null; this.index = 0; this.ref = null; // 新的變動(dòng)帶來(lái)的新的props this.pendingProps = pendingProps; // 上次渲染完成后的props this.memoizedProps = null; // 該Fiber對(duì)應(yīng)的組件產(chǎn)生的update會(huì)存放在這個(gè)隊(duì)列(比如setState和forceUpdate創(chuàng)建的更新) this.updateQueue = null; // 上一次的state this.memoizedState = null; this.dependencies = null; // this.mode = mode; // Effects // 用來(lái)記錄副作用 this.effectTag = NoEffect; // 單鏈表用來(lái)快速查找下一個(gè)side effect this.nextEffect = null; // 子樹(shù)中第一個(gè)side effect this.firstEffect = null; // 子樹(shù)中最后一個(gè)side effect this.lastEffect = null; // 代表任務(wù)在未來(lái)的哪個(gè)時(shí)候應(yīng)該被完成 就是過(guò)期時(shí)間 // 不包括他的子樹(shù)產(chǎn)生的任務(wù) this.expirationTime = NoWork; // 快速確定子樹(shù)中是否有不再等待的變化 this.childExpirationTime = NoWork; // Fiber樹(shù)更新過(guò)程中,每個(gè)FIber都會(huì)有一個(gè)跟其對(duì)應(yīng)的Fiber // 我們稱(chēng)他為`current <==> workInProgress` // 渲染完成后他們會(huì)交換位置 this.alternate = null; // 調(diào)試相關(guān)的去掉了 }
updateQueue
initializeUpdateQueue(uninitializedFiber);
位于:react-reconciler/src/ReactUpdateQueue.js
作用:?jiǎn)蜗蜴湵?,用?lái)存放update,next來(lái)串聯(lián)update
關(guān)于Update和UpdateQueue涉及到的東西比較多打算單獨(dú)一章來(lái)講解
export function initializeUpdateQueue<State>(fiber: Fiber): void { const queue: UpdateQueue<State> = { // 每次操作完更新阿之后的state baseState: fiber.memoizedState, // 隊(duì)列中的第一個(gè)`Update` firstBaseUpdate: null, // 隊(duì)列中的最后一個(gè)`Update` lastBaseUpdate: null, shared: { pending: null, }, effects: null, }; fiber.updateQueue = queue; }
流程圖
最后是畫(huà)的大致流程圖
以上就是ReactDOM.render在react源碼中執(zhí)行原理的詳細(xì)內(nèi)容,更多關(guān)于react ReactDOM.render執(zhí)行原理的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
詳解使用React進(jìn)行組件庫(kù)開(kāi)發(fā)
本篇文章主要介紹了詳解使用React進(jìn)行組件庫(kù)開(kāi)發(fā),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-02-02React?split實(shí)現(xiàn)分割字符串的使用示例
當(dāng)我們需要將一個(gè)字符串按照指定的分隔符進(jìn)行分割成數(shù)組時(shí),我們可以在組件的生命周期方法中使用split方法來(lái)實(shí)現(xiàn)這個(gè)功能,本文就來(lái)介紹一下,感興趣的可以了解下2023-10-10React Native實(shí)現(xiàn)簡(jiǎn)單的登錄功能(推薦)
這篇文章主要介紹了React Native實(shí)現(xiàn)登錄功能的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-09-09如何解決React官方腳手架不支持Less的問(wèn)題(小結(jié))
這篇文章主要介紹了如何解決React官方腳手架不支持Less的問(wèn)題(小結(jié)),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-09-09基于PixiJS實(shí)現(xiàn)react圖標(biāo)旋轉(zhuǎn)動(dòng)效
PixiJS是一個(gè)開(kāi)源的基于web的渲染系統(tǒng),為游戲、數(shù)據(jù)可視化和其他圖形密集型項(xiàng)目提供了極快的性能,這篇文章主要介紹了用PixiJS實(shí)現(xiàn)react圖標(biāo)旋轉(zhuǎn)動(dòng)效,需要的朋友可以參考下2022-05-05