React Fiber原始碼分析 第一篇
先附上流程圖一張
先由babel編譯, 呼叫reactDOM.render,入參為element, container, callback, 打印出來可以看到element,container,callback分別代表著react元素、DOM原生元素,回撥函式
render實際上呼叫的是 legacyRenderSubtreeIntoContainer函式
render: function (element, container, callback) { return legacyRenderSubtreeIntoContainer(null, element, container, false, callback); }
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 { ...... } }
從程式碼中看出, 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); }
從ReactRoot中, 我們把createContainer返回值賦給了 例項的_internalRoot, 往下看createContainer
function ReactRoot(container, isAsync, hydrate) { var root = createContainer(container, isAsync, hydrate); this._internalRoot = root; }
從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;
最後是返回了一個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; ... }
初始化完成, 接下來就是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; };
updateContsainer裡面使用了 currentTime 和 expirationTime,
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); }
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新版本非同步渲染的核心了, 留在下一篇繼續解讀