ReactRoot與ReactWork源碼分析

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

ReactRoot

這個類主要介紹其構造函數和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

  • new ReactWork -> work
  • 調用work的then方法
  • 調用updateContainer
  • 返回work

這裏值得注意的是ReactWork類,這個將在後文分析;還有updateContainer,這個將在後面的文章分析,這裏搞清楚第一調用時的參數給的啥,即ReactElement,createContainer返回的root,null,ReactWork實例的_onCommit方法。

ReactWork

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也是一個參與後面調度的關鍵。

相關文章
相關標籤/搜索