原文:Inside Fiber: in-depth overview of the new reconciliation algorithm in Reactreact
譯文:React Fiber 那些事: 深刻解析新的協調算法算法
上面提供了兩篇很是優秀的介紹React協調算法的文章,建議讀者先看原文或者譯文,看完一遍以後若是沒有太深的印象,那麼再從本文提供的角度去思考問題。segmentfault
在 《剖析React內部運行機制-「譯」React組件、元素和實例》 這篇文章中咱們知道:瀏覽器
那麼如何將應用程序中衆多的 「React元素」 組裝成 樹 就是協調算法核心工做的一部分。數據結構
注意: 瀏覽器只是React能夠渲染的「渲染環境」之一,其餘主要的目標是經過React native實現的iOS和Android視圖(這就是爲何「虛擬DOM」有點用詞不當)。在 《剖析React內部運行機制-「譯」React Fiber 架構》 這篇文章的 「協調與渲染」 部分有更加詳細的介紹。架構
React Fiber,其構造函數爲:ide
function FiberNode(tag, pendingProps, key, mode) {
// 此處屬性用於生成DOM節點實例
this.tag = tag; // 用於標識組件的類型,好比class組件、function組件、宿主組件等
this.key = key;
this.elementType = null;
this.type = null;
this.stateNode = null;
// 此處屬性用於協調算法遍歷Fiber節點樹
this.return = null;
this.child = null;
this.sibling = null;
this.index = 0;
this.ref = null;
this.pendingProps = pendingProps;
this.memoizedProps = null;
this.updateQueue = null;
this.memoizedState = null;
this.dependencies = null;
this.mode = mode;
// 此處屬性用於反作用(的調用,好比生命週期函數等)
this.effectTag = NoEffect;
this.nextEffect = null;
this.firstEffect = null;
this.lastEffect = null;
this.expirationTime = NoWork;
this.childExpirationTime = NoWork;
this.alternate = null;
if (enableProfilerTimer) {
// 下面的操做是爲了不v8性能問題
this.actualDuration = Number.NaN;
this.actualStartTime = Number.NaN;
this.selfBaseDuration = Number.NaN;
this.treeBaseDuration = Number.NaN;
this.actualDuration = 0;
this.actualStartTime = -1;
this.selfBaseDuration = 0;
this.treeBaseDuration = 0;
}
{
this._debugID = debugCounter++;
this._debugSource = null;
this._debugOwner = null;
this._debugIsCurrentlyTiming = false;
this._debugNeedsRemount = false;
this._debugHookTypes = null;
if (!hasBadMapPolyfill && typeof Object.preventExtensions === 'function') {
Object.preventExtensions(this);
}
}
}
複製代碼
固然,React Fiber的屬性用的最多的仍是前半部分。其更加詳細做用在《剖析React內部運行機制-「譯」React Fiber 架構》 這篇文章中有介紹。函數
一、協調算法入口函數-renderRootoop
function renderRoot(root, expirationTime, isSync) {
...
// 該方法內部會建立workInProgress,並暴露到外層(即renderRoot)做用域
prepareFreshStack(root, expirationTime);
...
// 開始執行工做循環
if (isSync) {
workLoopSync();
} else {
workLoop();
}
...
// 檢查工做循環結束後的workInProgress狀態
switch (workInProgressRootExitStatus) {
...
case RootCompleted:
return commitRoot.bind(null, root);
}
}
複製代碼
二、工做循環/組件解析-workLoopSyncpost
function workLoopSync() {
while (workInProgress !== null) {
workInProgress = performUnitOfWork(workInProgress);
}
}
複製代碼
三、執行工做單元-performUnitOfWork
function performUnitOfWork(unitOfWork) {
var current$$1 = unitOfWork.alternate;
...
var next = void 0;
next = beginWork$$1(current$$1, unitOfWork, renderExpirationTime);
...
return next;
}
複製代碼
四、開始工做--beginWork$$1(其實是執行的是下面beginWork$1)
function beginWork$1(current$$1, workInProgress, renderExpirationTime) {
...
// 這裏根據當前組件的類型分別返回不一樣的內容
switch (workInProgress.tag) {
...
case FunctionComponent:
return updateFunctionComponent(current$$1, workInProgress, _Component, resolvedProps, renderExpirationTime);
case ClassComponent:
return updateClassComponent(current$$1, workInProgress, _Component2, _resolvedProps, renderExpirationTime);
case HostRoot:
return updateHostRoot(current$$1, workInProgress, renderExpirationTime);
case HostComponent:
return updateHostComponent(current$$1, workInProgress, renderExpirationTime);
case HostText:
return updateHostText(current$$1, workInProgress);
...
}
}
複製代碼
函數 beginWork 始終返回指向要在循環中處理的下一個子節點的指針或 null。
五、當匹配到ClassComponent
return updateClassComponent(current$$1, workInProgress, _Component2, _resolvedProps, renderExpirationTime);
複製代碼
更新組件函數-updateClassComponent
function updateClassComponent(current$$1, workInProgress, Component, nextProps, renderExpirationTime) {
...
var nextUnitOfWork = finishClassComponent(current$$1, workInProgress, Component, shouldUpdate, hasContext, renderExpirationTime);
return nextUnitOfWork;
}
複製代碼
六、結束組件函數-finishClassComponent
function finishClassComponent(current$$1, workInProgress, Component, shouldUpdate, hasContext, renderExpirationTime) {
...
var instance = workInProgress.stateNode;
...
var nextChildren = void 0;
...
// 這裏會調用React組件裏面的render函數獲取返回值
nextChildren = instance.render();
...
// 協調算法的核心--查找child節點
reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime)
...
return workInProgress.child;
}
複製代碼
七、協調算法核心之一-肯定child節點
function reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime) {
if (current$$1 === null) {
workInProgress.child = mountChildFibers(workInProgress, null, nextChildren, renderExpirationTime);
} else {
workInProgress.child = reconcileChildFibers(workInProgress, current$$1.child, nextChildren, renderExpirationTime);
}
}
複製代碼
繼續看下面函數的定義,瞭解workInProgress.child是被賦了什麼值。
// mountChildFibers和reconcileChildFibers調用了同一個方法,分別是首次加載和後續更新時的執行邏輯
var reconcileChildFibers = ChildReconciler(true);
var mountChildFibers = ChildReconciler(false);
function ChildReconciler(shouldTrackSideEffects) {
function deleteChild(returnFiber, childToDelete) {
...
}
...
// 協調單個元素(協調核心方法也會調用這裏哦)
function reconcileSingleElement(returnFiber, currentFirstChild, element, expirationTime) {
var key = element.key;
var child = currentFirstChild;
while (child !== null) {
...
child = child.sibling;
}
if (element.type === REACT_FRAGMENT_TYPE) {
// 這裏經過函數名就能夠知道,createFiberFromFragment將根據render函數返回值裏面的Fragment元素建立Fiber對象。
var created = createFiberFromFragment(element.props.children, returnFiber.mode, expirationTime, element.key);
created.return = returnFiber;
// 天哪!終於有返回值了,是一個Fiber節點對象
return created;
} else {
// createFiberFromElement將根據render函數返回值裏面的DOM元素建立Fiber對象
var _created4 = createFiberFromElement(element, returnFiber.mode, expirationTime);
_created4.ref = coerceRef(returnFiber, currentFirstChild, element);
_created4.return = returnFiber;
// 返回一個Fiber節點對象
return _created4;
}
}
...
// 這裏纔是核心方法
function reconcileChildFibers(returnFiber, currentFirstChild, newChild, expirationTime) {
...
// 處理對象類型
var isObject = typeof newChild === 'object' && newChild !== null;
if (isObject) {
switch (newChild.$$typeof) {
case REACT_ELEMENT_TYPE:
// 注意這裏哦,會調用上面定義的函數
return placeSingleChild(reconcileSingleElement(returnFiber, currentFirstChild, newChild, expirationTime));
case REACT_PORTAL_TYPE:
return placeSingleChild(reconcileSinglePortal(returnFiber, currentFirstChild, newChild, expirationTime));
}
}
...
return reconcileChildFibers;
}
複製代碼
完成每件有意義的事情都會須要咱們不懈的堅持。 若是你堅持看到了這裏,請爲本身點個贊。
在React協調算法執行過程當中,會遇到不少不少的case
,在這篇文章中咱們僅僅是對ClassComponent
類型的組件抓住一條主線進行深刻的剖析。其餘類型好比HostRoot
、HostComponent
等能夠按照上述分析思路進行探索。
文章開頭提供的兩篇文章對「協調算法」作了很好的理論分析,在理論分析的基礎上,在React內部執行到renderRoot()
函數內部時,經過逐級剖析源碼中相關函數的主體(省略了不少判斷以及校驗等邏輯),分析了root
對象上面的組件如何一步步被解析成對應的React元素樹。