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