摘要:本文將對(duì)源碼做一個(gè)初步解析。首先在方法中校驗(yàn)參數(shù)是否合法,然后調(diào)用在中,調(diào)用拿到了的一個(gè)實(shí)例,調(diào)用拿到了,用于注入到,和作為返回值,調(diào)用開(kāi)始調(diào)度過(guò)程在中,首先清理了中的所有子節(jié)點(diǎn),然后了一個(gè)并返回是如何調(diào)度的是一個(gè)什么樣的類(lèi)的操作是在哪里
初步看了react-dom這個(gè)包的一些源碼,發(fā)現(xiàn)其比react包要復(fù)雜得多,react包中基本不存在跨包調(diào)用的情況,他所做的也僅僅是定義了ReactElement對(duì)象,封裝了對(duì)ReactElement的基本操作,而react-dom包存在復(fù)雜的函數(shù)調(diào)用。本文將對(duì)ReactDOM.render源碼做一個(gè)初步解析。前言
文章中如有不當(dāng)之處,歡迎交流指點(diǎn)。react版本16.8.2。在源碼添加的注釋在githubreact-source-learn。
使用react時(shí)常常寫(xiě)類(lèi)似下面的代碼:
import ReactDOM from "react-dom"; ReactDOM.render(Hello, world!
, document.getElementById("root") );
代碼1
這里導(dǎo)入的ReactDOM就是packages/react-dom/client/ReactDOM.js中所導(dǎo)出的對(duì)象。從文檔可見(jiàn)ReactDOM對(duì)象有如下幾個(gè)方法:(ps:從源碼看其實(shí)還有很多其他方法)
render()
hydrate()
unmountComponentAtNode()
findDOMNode()
createPortal()
本文只介紹render()方法
代碼分析render方法定義如下:
render( element: React$Element, container: DOMContainer, callback: ?Function, ) { invariant( // 1 isValidContainer(container), "Target container is not a DOM element.", ); if (__DEV__) { warningWithoutStack( !container._reactHasBeenPassedToCreateRootDEV, "You are calling ReactDOM.render() on a container that was previously " + "passed to ReactDOM.%s(). This is not supported. " + "Did you mean to call root.render(element)?", enableStableConcurrentModeAPIs ? "createRoot" : "unstable_createRoot", ); } // 2 return legacyRenderSubtreeIntoContainer( null, element, container, false, callback, ); },
代碼2
render方法接收兩個(gè)必選參數(shù)可一個(gè)可選參數(shù),結(jié)合代碼1的調(diào)用可知,element是一個(gè)ReactElement對(duì)象, container是一個(gè)dom節(jié)點(diǎn),callback在上面的代碼1并沒(méi)有指定,他是一個(gè)可選函數(shù)。
這個(gè)render方法做的事情比較簡(jiǎn)單,一是校驗(yàn)container參數(shù),二是調(diào)用legacyRenderSubtreeIntoContainer方法并返回。
接下來(lái)是legacyRenderSubtreeIntoContainer
// 刪除了第一次調(diào)ReactDOM.render不會(huì)走的分支 function legacyRenderSubtreeIntoContainer( parentComponent: ?React$Component, // ReactDOM.render 是null children: ReactNodeList, // 是一個(gè)ReactElement , ReactDOM.render是第一個(gè)參數(shù) container: DOMContainer, // 是一個(gè)dom節(jié)點(diǎn), ReactDOM.render是第二個(gè)參數(shù) forceHydrate: boolean, // ReactDOM.render 是false callback: ?Function, // ReactDOM.render 是 第三個(gè)參數(shù) ) { if (__DEV__) { topLevelUpdateWarnings(container); } // TODO: Without `any` type, Flow says "Property cannot be accessed on any // member of intersection type." Whyyyyyy. // 根據(jù)type知道, Root type是個(gè)對(duì)象,包含 // render方法 // unmount方法 // legacy_renderSubtreeIntoContainer 方法 // createBatch 方法 // _internalRoot屬性 let root: Root = (container._reactRootContainer: any); if (!root) { // ReactDOM.render調(diào)用時(shí)走這里 // Initial mount // 調(diào)用 legacyCreateRootFromDOMContainer 拿 Root root = container._reactRootContainer = legacyCreateRootFromDOMContainer( container, forceHydrate, // ReactDOM.render是false ); // 在callback加參數(shù) if (typeof callback === "function") { const originalCallback = callback; callback = function() { const instance = getPublicRootInstance(root._internalRoot); originalCallback.call(instance); }; } // Initial mount should not be batched. // 這個(gè)是packages/react-reconciler/ReactFiberScheduler.js中的方法 // TOLEARN: 這個(gè)里邊應(yīng)該是一些調(diào)度過(guò)程, 后續(xù)再看 unbatchedUpdates(() => { if (parentComponent != null) { root.legacy_renderSubtreeIntoContainer( parentComponent, children, callback, ); } else { // ReactDOM.render方法走這里 // 這里的root.render 返回的是一個(gè)叫Work的東西, TOLEARN,這個(gè)Work后面再做了解 root.render(children, callback); } }); } // getPublicRootInstance是/packages/react-reconciler/ReactFiberReconciler.js中的方法 // 關(guān)于他是返回的一個(gè)什么東西, 后面再看, 總之他是我ReactDOM.render方法回調(diào)函數(shù)的一個(gè)參數(shù), // 也是返回值 return getPublicRootInstance(root._internalRoot); }
legacyRenderSubtreeIntoContainer在第一次render時(shí)做了如下事情:
調(diào)用legacyCreateRootFromDOMContainer拿到一個(gè)ReactRoot的實(shí)例
在callback中注入一個(gè)參數(shù)instance
調(diào)用unbatchedUpdates開(kāi)始一輪調(diào)度過(guò)程,這個(gè)是猜的
返回instance
關(guān)于instance的獲取是調(diào)用的getPublicRootInstance,getPublicRootInstance是/packages/react-reconciler/ReactFiberReconciler.js中的方法,后面再研究
關(guān)于unbatchedUpdates, 這個(gè)東西是這個(gè)是packages/react-reconciler/ReactFiberScheduler.js中的方法,簡(jiǎn)單看了看還沒(méi)太明白
接下來(lái)看一下ReactRoot實(shí)例的獲取
// 刪除了__DEV__分支的代碼 // 若需要清理container的子節(jié)點(diǎn),清理, 然后new ReactRoot并返回 function legacyCreateRootFromDOMContainer( container: DOMContainer, // dom節(jié)點(diǎn) forceHydrate: boolean, // false render ): Root { // 是否不需要清理container的子元素, 第一次render是false, 即需要清理 const shouldHydrate = forceHydrate || shouldHydrateDueToLegacyHeuristic(container); // 第一次調(diào)用時(shí)為false // First clear any existing content. if (!shouldHydrate) { // 第一次render走這里 let warned = false; let rootSibling; // 這里將container的子元素都清理掉了 while ((rootSibling = container.lastChild)) { container.removeChild(rootSibling); } } // Legacy roots are not async by default. const isConcurrent = false; return new ReactRoot(container, isConcurrent, shouldHydrate); }
這個(gè)方法比較簡(jiǎn)單,在第一次調(diào)用ReactDOM.render時(shí),shouldHydrate會(huì)是false,所以會(huì)走到if (!shouldHydrate) 分支里,將container節(jié)點(diǎn)的所有子節(jié)點(diǎn)都清理掉,最后是new 了一個(gè)ReactRoot作為返回值,關(guān)于ReactRoot等內(nèi)容將放到后面的文章分析。
小結(jié)以上是ReactDOM.render的函數(shù)調(diào)用示意圖。
首先在render方法中校驗(yàn)container參數(shù)是否合法,然后調(diào)用legacyRenderSubtreeIntoContainer
在legacyRenderSubtreeIntoContainer中, 調(diào)用legacyCreateRootFromDOMContainer拿到了ReactRoot的一個(gè)實(shí)例,調(diào)用getPublicRootInstance拿到了instance,用于注入到callback,和作為返回值,調(diào)用unbatchedUpdates開(kāi)始調(diào)度過(guò)程
在legacyCreateRootFromDOMContainer中,首先清理了container中的所有子節(jié)點(diǎn),然后new了一個(gè)ReactRoot并返回
TODOunbatchedUpdates是如何調(diào)度的
ReactRoot是一個(gè)什么樣的類(lèi)
dom的操作是在哪里
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/103511.html
摘要:查看創(chuàng)建核心函數(shù)源碼行調(diào)用函數(shù)創(chuàng)建是相關(guān),不用管源碼行這個(gè)指的是調(diào)用創(chuàng)建,下面我們將會(huì)說(shuō)到對(duì)象源碼行源碼行函數(shù)中,首先創(chuàng)建了一個(gè),然后又創(chuàng)建了一個(gè),它們兩者還是相互引用。 感謝 yck: 剖析 React 源碼解析,本篇文章是在讀完他的文章的基礎(chǔ)上,將他的文章進(jìn)行拆解和加工,加入我自己的一下理解和例子,便于大家理解。覺(jué)得yck寫(xiě)的真的很棒 。React 版本為 16.8.6,關(guān)于源碼的...
摘要:一更新的方式有三種渲染接下來(lái),我們就來(lái)看下源碼二作用在提供的里渲染一個(gè)元素,并返回對(duì)該組件的引用常見(jiàn)的用法是這個(gè)官網(wǎng)網(wǎng)址源碼服務(wù)端使用方法渲染節(jié)點(diǎn)是讓服務(wù)端盡可能復(fù)用節(jié)點(diǎn),提高性能元素容器應(yīng)用渲染結(jié)束后,調(diào)用的函數(shù)錯(cuò)誤抓取方法本質(zhì)是返回 showImg(https://segmentfault.com/img/remote/1460000020064414?w=1240&h=641);...
摘要:的創(chuàng)建組件,其實(shí)根源還是調(diào)用了編譯之后一般寫(xiě)法建議用來(lái)進(jìn)行源碼的跟蹤鏈接從源碼角度來(lái)看創(chuàng)建一個(gè)組件的過(guò)程中發(fā)生了什么。 https://github.com/jimwmg/Rea... 1 React.createClass( ) var HelloWorld = React.createClass({ render : function(){ return ...
摘要:在中調(diào)用獲得了的實(shí)例,然后調(diào)用其中的回調(diào)函數(shù)中調(diào)用了方法。這個(gè)類(lèi)主要介紹其構(gòu)造函數(shù)和方法,構(gòu)造函數(shù)是時(shí)調(diào)用的,方法是的回調(diào)函數(shù)中使用的。這個(gè)將在下一篇分析。另外,方法是在的回調(diào)函數(shù)中調(diào)用的,也是一個(gè)參與后面調(diào)度的關(guān)鍵。 在ReactDOM.render源碼解析-1中介紹了第一次render的基本過(guò)程的一部分,其中產(chǎn)生了ReactRoot和ReactWork兩個(gè)類(lèi)的實(shí)例。本文介紹下Rea...
摘要:就是,如果你不了解這個(gè)的話可以閱讀下相關(guān)文檔,是應(yīng)用初始化時(shí)就會(huì)生成的一個(gè)變量,值也是,并且這個(gè)值不會(huì)在后期再被改變。這是我的剖析 React 源碼的第三篇文章,如果你沒(méi)有閱讀過(guò)之前的文章,請(qǐng)務(wù)必先閱讀一下 第一篇文章 中提到的一些注意事項(xiàng),能幫助你更好地閱讀源碼。 文章相關(guān)資料 React 16.8.6 源碼中文注釋?zhuān)@個(gè)鏈接是文章的核心,文中的具體代碼及代碼行數(shù)都是依托于這個(gè)倉(cāng)庫(kù) 熱身...
閱讀 881·2023-04-26 03:03
閱讀 2206·2021-10-12 10:12
閱讀 1201·2021-09-24 09:48
閱讀 1645·2021-09-22 15:25
閱讀 3332·2021-09-22 15:15
閱讀 914·2019-08-29 16:21
閱讀 1064·2019-08-28 18:00
閱讀 3423·2019-08-26 13:44