React Fiber原始碼分析 第一篇

菜的黑人牙膏發表於2019-01-28

系列文章

React Fiber原始碼分析 第一篇
React Fiber原始碼分析 第二篇(同步模式)
React Fiber原始碼分析 第三篇(非同步狀態)
React Fiber原始碼分析 第四篇(歸納總結)

前言

React Fiber是React在V16版本中的大更新,利用了閒餘時間看了一些原始碼,做個小記錄~

流程圖

image

原始碼分析

先由babel編譯, 呼叫reactDOM.render,入參為element, container, callback, 列印出來可以看到element,container,callback分別代表著react元素、DOM原生元素,回撥函式

image

1.render實際上呼叫的是legacyRenderSubtreeIntoContainer函式

render: function (element, container, callback) {
  return legacyRenderSubtreeIntoContainer(null, element, container, false, callback);
}
複製程式碼

2.legacyRenderSubtreeIntoContainer 這個函式, 實際上是初始化了root, 並呼叫了root.render方法, 而root是由legacyCreateRootFromDOMContainer函式返回的

function legacyRenderSubtreeIntoContainer(parentComponent, children, container, forceHydrate, callback) {
  var root = container._reactRootContainer;
  if (!root) {
    // 初始化root
    root = container._reactRootContainer = legacyCreateRootFromDOMContainer(container, forceHydrate);// Initial mount should not be batched.
    unbatchedUpdates(function () {
      if (parentComponent != null) {
        root.legacy_renderSubtreeIntoContainer(parentComponent, children, callback);
      } else {
        // 呼叫root的render方法
        root.render(children, callback);
      }
    });
  } else {
    ......
  }
}
複製程式碼

3.從程式碼中看出, legacyCreateRootFromDOMContainer執行了兩個操作, 一個是清除掉所有的子元素, 另外一個則是返回了一個 ReactRoot例項, 這裡需要注意一點, root預設是同步更新的, 即isAsync 預設為false

function legacyCreateRootFromDOMContainer(container, forceHydrate) {
  ...// 清除所有子元素
  if (!shouldHydrate) {
    var warned = false;
    var rootSibling = void 0;
    while (rootSibling = container.lastChild) {
      {
        if (!warned && rootSibling.nodeType === ELEMENT_NODE && rootSibling.hasAttribute(ROOT_ATTRIBUTE_NAME)) {
          warned = true;
        }
      }
      container.removeChild(rootSibling);
    }
  }// 預設為同步狀態
  var isAsync = false;
  return new ReactRoot(container, isAsync, shouldHydrate);
}
複製程式碼

4.從ReactRoot中, 我們把createContainer返回值賦給了 例項的**_internalRoot**, 往下看createContainer

function ReactRoot(container, isAsync, hydrate) {
  var root = createContainer(container, isAsync, hydrate);
  this._internalRoot = root;
}
複製程式碼

5.從createContainer看出, createContainer實際上是直接返回了createFiberRoot, 而createFiberRoot則是通過createHostRootFiber函式的返回值uninitializedFiber,並將其賦值在root物件的current上, 這裡需要注意一個點就是,uninitializedFiber的stateNode的值是root, 即他們互相引用

function createContainer(containerInfo, isAsync, hydrate) {
  return createFiberRoot(containerInfo, isAsync, hydrate);
}
function createFiberRoot(containerInfo, isAsync, hydrate) {
  // 建立hostRoot並賦值給uninitiallizedFiber
  var uninitializedFiber = createHostRootFiber(isAsync);
  // 互相引用
  var root = void 0;
  root = {
      current: uninitializedFiber,
      ...
  };
 uninitializedFiber.stateNode = root; 
複製程式碼

6.最後是返回了一個fiberNode的例項, 在這裡我們可以看到mode這個欄位, 由於在一開始就將isAsync初始化為false, 所以mode實際上就代表了同步

在這裡, 整理一下各個例項的關係,

root為ReactRoot例項

root._internalRoot 即為fiberRoot例項

root._internalRoot.current即為Fiber例項

root._internalRoot.current.stateNode = root._internalRoot


function createHostRootFiber(isAsync) {
  var mode = isAsync ? AsyncMode | StrictMode : NoContext;
  return createFiber(HostRoot, null, null, mode);
}
var createFiber = function (tag, pendingProps, key, mode) {
  return new FiberNode(tag, pendingProps, key, mode);
};
function FiberNode(tag, pendingProps, key, mode) {
  // Instance
  this.tag = tag;
  this.key = key;
  this.type = null;
  this.stateNode = null;

  // Fiber
  this.return = null;
  this.child = null;
  this.sibling = null;
  this.index = 0;

  ...
}
複製程式碼

7.初始化完成, 接下來就是root.render執行了, 在這裡, 先暫時忽略ReactWork, 把work._onCommit當成一個回撥函式即可, 可以看到, root即FiberRoot例項被當成引數傳入了updateContsainer裡面, 往下看updateContainer

ReactRoot.prototype.render = function (children, callback) {
  var root = this._internalRoot;
  var work = new ReactWork();
  callback = callback === undefined ? null : callback;
  if (callback !== null) {
    work.then(callback);
  }
  updateContainer(children, root, null, work._onCommit);
  return work;
};
複製程式碼

8.updateContsainer裡面使用了 currentTimeexpirationTime,

currentTime是用來計算expirationTime,

expirationTime代表著優先順序, 留在後續分析,

這裡我們知道是同步更新 即 expirationTime = 1. 緊接著呼叫了updateContainerAtExpirationTime


function updateContainer(element, container, parentComponent, callback) {
  var current$$1 = container.current;
  var currentTime = requestCurrentTime();
  var expirationTime = computeExpirationForFiber(currentTime, current$$1);
  return updateContainerAtExpirationTime(element, container, parentComponent, expirationTime, callback);
}
複製程式碼

9.updateContainerAtExpirationTime將current(即Fiber例項)提取出來, 並作為引數傳入呼叫scheduleRootUpdate

function updateContainerAtExpirationTime(element, container, parentComponent, expirationTime, callback) {
  // TODO: If this is a nested container, this won't be the root.
  var current$$1 = container.current;
  ...
  return scheduleRootUpdate(current$$1, element, expirationTime, callback);
}
複製程式碼

到了這裡告一段落, scheduleRootUpdate接下來就是React新版本非同步渲染的核心了, 留在下一篇繼續解讀

相關文章