在 ReactDOM.render源碼解析-1中介紹了第一次render的基本過程的一部分,其中產生了ReactRoot和ReactWork兩個類的實例。本文介紹下ReactRoot,ReactWork源碼,只關注第一次調用render的過程。
文章中若有不當之處,歡迎交流指點。react版本16.8.2
。在源碼添加的註釋在github react-source-learn。
在上篇分析後,最終獲得以下函數調用過程。react
在render方法中調用了legacyRenderSubtreeIntoContainer。git
在legacyRenderSubtreeIntoContainer中調用legacyCreateRootFromDOMContainer得到了ReactRoot的實例root,而後調用unbatchedUpdates,其中的回調函數中調用了root.render方法。RootRoot是什麼?他的render方法作了什麼?github
經過對ReactRoot和ReactWork代碼的簡單分析,筆者作了以下類圖,以幫助瞭解這兩個類有哪些屬性和方法。segmentfault
有不少東西不是第一次調用render用到的,這裏只關注第一次render所須要調用方法或使用的屬性。dom
這個類主要介紹其構造函數和render方法,構造函數是new時調用的,render方法是unbatchedUpdates的回調函數中使用的。代碼以下:函數
// ReactRoot構造函數 // 構造函數主要是掛了一個_internalRoot在this上 function ReactRoot( container: DOMContainer, // dom節點 isConcurrent: boolean, // 第一次render爲false hydrate: boolean, // 第一次render爲false ) { // 這個createContainer是packages/ReactFiberReconciler中的方法, // 返回的是一個OpaqueRoot的東西 const root = createContainer(container, isConcurrent, hydrate); this._internalRoot = root; } // render實例方法 new 了ReactWork, 調用了then方法 // 調用了updateContainer方法 // 返回了ReactWork實例 ReactRoot.prototype.render = function( children: ReactNodeList, // element callback: ?() => mixed, // ReactDOM.render(element, container, callback); callback ): Work { const root = this._internalRoot; const work = new ReactWork(); callback = callback === undefined ? null : callback; if (callback !== null) { work.then(callback); } // updateContainer是packages/react-reconciler/ReactFiberReconciler.js中的 // 一個方法,後邊再說 updateContainer(children, root, null, work._onCommit); return work; };
先看構造函數
第一次render時new ReactRoot位於legacyCreateRootFromDOMContainer中,其調用代碼以下:return new ReactRoot(container, isConcurrent, shouldHydrate)
。
container是咱們調用ReactDOM.render時的第二個參數,一個dom,可是裏邊的子節點已被處理了,isConcurrent這裏是寫死的false,shouldHydrate上文分析過,爲false。
再看ReactRoot的構造函數,他調用了一個createContainer並將返回值掛到了_internalRoot屬性。這個createContainer將在下一篇分析。this
看下render方法spa
咱們先看看第一次render是調用他的代碼,root.render(children, callback);
,其中children是ReactDOM.render的第一個參數,是個ReactElement, callback是第三個參數,一般不傳。prototype
這個render方法主要作了以下事:code
這裏值得注意的是ReactWork類,這個將在後文分析;還有updateContainer,這個將在後面的文章分析,這裏搞清楚第一調用時的參數給的啥,即ReactElement,createContainer返回的root,null,ReactWork實例的_onCommit方法。
ReactWork的方法在第一次render時都有可能被調用到,以下代碼爲ReactWork類的定義:
// ReactWork的構造函數 function ReactWork() { this._callbacks = null; this._didCommit = false; // TODO: Avoid need to bind by replacing callbacks in the update queue with // list of Work objects. this._onCommit = this._onCommit.bind(this); } // then方法 ReactWork.prototype.then = function(onCommit: () => mixed): void { if (this._didCommit) { // 第一次render調用then時爲false, 不走這裏 onCommit(); return; } let callbacks = this._callbacks; if (callbacks === null) { // 第一次render是調用走這裏 callbacks = this._callbacks = []; } callbacks.push(onCommit); }; // _onCommit方法 ReactWork.prototype._onCommit = function(): void { if (this._didCommit) { // 第一次render不走這裏 return; } this._didCommit = true; // 這個callbacks是調用.then方法是傳進去的函數 const callbacks = this._callbacks; if (callbacks === null) { return; } // TODO: Error handling. for (let i = 0; i < callbacks.length; i++) { const callback = callbacks[i]; invariant( typeof callback === 'function', 'Invalid argument passed as callback. Expected a function. Instead ' + 'received: %s', callback, ); callback(); } };
構造函數
構造函數不接受參數,作了一些初始化工做
then方法
then方法調用時是work.then(callback);
,callback是ReactDOM的第三個參數
then方法的做用就是維護一個_callbacks隊列,每次都將傳進去的函數入隊
_onCommit方法
這個方法的調用代碼updateContainer(children, root, null, work._onCommit)
,實際上是updateContainer的最後一個參數。
在這個裏邊將_didCommit置爲true,回顧上邊的ReactRoot的render方法,意味着這個方法被調用後在調ReactRoot.render是會直接執行callback的而不是入隊。
而後是將_callbacks中的方法都執行了一遍。
從上文的分析來看,接下來的重點是分析updateContainer這個方法,ReactWork的then方法是將callback入隊,_onCommit是執行_callbacks中的全部方法,而調用_onCommit的是在updateContainer中,updateContainer實在ReactRoot.render方法中調用的,所以updateContainer應該是一個很是重要的東西。另外,ReactRoo.render方法是在unbatchedUpdates的回調函數中調用的,unbatchedUpdates也是一個參與後面調度的關鍵。