国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

React16.2的fiber架構

lansheng228 / 1808人閱讀

摘要:司徒正美,加群一起研究與用于調整渲染順序,高優先級的組件先執行這只是一部分更新邏輯,簡直沒完沒了,下次繼續添上流程圖,回憶一下本文學到的東西

React16真是一天一改,如果現在不看,以后也很難看懂了。

在React16中,雖然也是通過JSX編譯得到一個虛擬DOM對象,但對這些虛擬DOM對象的再加工則是經過翻天覆地的變化。我們需要追根溯底,看它是怎么一步步轉換過來的。我們先不看什么組件render,先找到ReactDOM.render。在ReactDOM的源碼里,有三個類似的東西:

//by 司徒正美, 加群:370262116 一起研究React與anujs
// https://github.com/RubyLouvre/anu 歡迎加star

ReactDOM= {
 hydrate: function (element, container, callback) {
    //新API,代替render
    return renderSubtreeIntoContainer(null, element, container, true, callback);
  },
  render: function (element, container, callback) {
    //React15的重要API,逐漸退出舞臺
    return renderSubtreeIntoContainer(null, element, container, false, callback);
  },
  unstable_renderSubtreeIntoContainer: function (parentComponent, element, containerNode, callback) {
    //用于生成子樹,廢棄
    return renderSubtreeIntoContainer(parentComponent, element, containerNode, false, callback);
  }
}

我們看renderSubtreeIntoContainer,這是一個內部API

//by 司徒正美, 加群:370262116 一起研究React與anujs

function renderSubtreeIntoContainer(parentComponent, children, container, forceHydrate, callback) {

  var root = container._reactRootContainer;
  if (!root) {
    //如果是第一次對這個元素進行渲染,那么它會清空元素的內部
    var shouldHydrate = forceHydrate || shouldHydrateDueToLegacyHeuristic(container);
    // First clear any existing content.
    if (!shouldHydrate) {
      var warned = false;
      var rootSibling = void 0;
      while (rootSibling = container.lastChild) {
        container.removeChild(rootSibling);
      }
    }

    var newRoot = DOMRenderer.createContainer(container, shouldHydrate);
    //創建一個HostRoot對象,是Fiber對象的一種
    root = container._reactRootContainer = newRoot;
    
    // Initial mount should not be batched.
    DOMRenderer.unbatchedUpdates(function () {
     //對newRoot對象進行更新
      DOMRenderer.updateContainer(children, newRoot, parentComponent, callback);
    });
  } else {
    //對root對象進行更新
    DOMRenderer.updateContainer(children, root, parentComponent, callback);
  }
  return DOMRenderer.getPublicRootInstance(root);
}

看一下DOMRenderer.createContainer是怎么創建root對象的。

首先DOMRenderer這個對象是由一個叫reactReconciler的方法生成,需要傳入一個對象,將一些東西注進去。最后產生一個對象,里面就有createContainer這個方法

// containerInfo就是ReactDOM.render(
, containerInfo)的第二個對象,換言之是一個元素節點 createContainer: function (containerInfo, hydrate) { return createFiberRoot(containerInfo, hydrate); },

再看createFiberRoot是怎么將一個真實DOM變成一個Fiber對象

//by 司徒正美, 加群:370262116 一起研究React與anujs

function createFiberRoot(containerInfo, hydrate) {
  // Cyclic construction. This cheats the type system right now because
  // stateNode is any.
  var uninitializedFiber = createHostRootFiber();
  var root = {
    current: uninitializedFiber,
    containerInfo: containerInfo,
    pendingChildren: null,
    remainingExpirationTime: NoWork,
    isReadyForCommit: false,
    finishedWork: null,
    context: null,
    pendingContext: null,
    hydrate: hydrate,
    nextScheduledRoot: null
  };
  uninitializedFiber.stateNode = root;

  return root;
}

function createHostRootFiber() {
  var fiber = createFiber(HostRoot, null, NoContext);
  return fiber;
}

var createFiber = function (tag, key, internalContextTag) {
  return new FiberNode(tag, key, internalContextTag);
};


function FiberNode(tag, key, internalContextTag) {
  // 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;

  this.ref = null;

  this.pendingProps = null;
  this.memoizedProps = null;
  this.updateQueue = null;
  this.memoizedState = null;

  this.internalContextTag = internalContextTag;

  // Effects
  this.effectTag = NoEffect;
  this.nextEffect = null;

  this.firstEffect = null;
  this.lastEffect = null;

  this.expirationTime = NoWork;

  this.alternate = null;


}

所有Fiber對象都是FiberNode的實例,它有許多種類型,通過tag來標識。

內部有許多方法來生成Fiber對象

createFiberFromElement (type為類,無狀態函數,元素標簽名)

createFiberFromFragment (type為React.Fragment)

createFiberFromText (在JSX中表現為字符串,數字)

createFiberFromHostInstanceForDeletion

createFiberFromCall

createFiberFromReturn

createFiberFromPortal (createPortal就會產生該類型)

createFiberRoot (用于ReactDOM.render的根節點)

createFiberRoot就是創建了一個普通對象,里面有一個current屬性引用fiber對象,有一個containerInfo屬性引用剛才的DOM節點,然后fiber對象有一個stateNode引用剛才的普通對象。在React15中,stateNode應該是一個組件實例或真實DOM,可能單純是為了對齊,就創建一個普通對象。 最后返回普通對象。

我們先不看 DOMRenderer.unbatchedUpdates,直接看DOMRenderer.updateContainer。

//children就是ReactDOM的第一個參數,children通常表示一個數組,但是現在它泛指各種虛擬DOM了,第二個對象就是剛才提到的普通對象,我們可以稱它為根組件,parentComponent為之前的根組件,現在它為null
 DOMRenderer.updateContainer(children, newRoot, parentComponent, callback);

updateContainer的源碼也很簡單,就是獲得上下文對象,決定它是叫context還是pendingContext,最后丟給scheduleTopLevelUpdate

//by 司徒正美, 加群:370262116 一起研究React與anujs

 updateContainer: function (element, container, parentComponent, callback) {
      var current = container.current;//createFiberRoot中創建的fiber對象
      var context = getContextForSubtree(parentComponent);
      if (container.context === null) {
        container.context = context;
      } else {
        container.pendingContext = context;
      }
      // 原傳名為 children, newRoot, parentComponent, callback
      // newRoot.fiber, children, callback
      scheduleTopLevelUpdate(current, element, callback);
    },

getContextForSubtree的實現

//by 司徒正美, 加群:370262116 一起研究React與anujs

function getContextForSubtree(parentComponent) {
  if (!parentComponent) {
    return emptyObject_1;
  }

  var fiber = get(parentComponent);
  var parentContext = findCurrentUnmaskedContext(fiber);
  return isContextProvider(fiber) ? processChildContext(fiber, parentContext) : parentContext;
}
//isContextConsumer與isContextProvider是兩個全新的概念,
// 從原上下文中抽取一部分出來
function isContextConsumer(fiber) {
  return fiber.tag === ClassComponent && fiber.type.contextTypes != null;
}
//isContextProvider,產生一個新的上下文
function isContextProvider(fiber) {
  return fiber.tag === ClassComponent && fiber.type.childContextTypes != null;
}

function _processChildContext(currentContext) {
    var Component = this._currentElement.type;
    var inst = this._instance;
    var childContext;
    if (inst.getChildContext) {
       childContext = inst.getChildContext();
    }
    
    if (childContext) {
        return _assign({}, currentContext, childContext);
    }
    return currentContext;
}

function findCurrentUnmaskedContext(fiber) {
 
  var node = fiber;
  while (node.tag !== HostRoot) {
    if (isContextProvider(node)) {
      return node.stateNode.__reactInternalMemoizedMergedChildContext;
    }
    var parent = node["return"];
    node = parent;
  }
  return node.stateNode.context;
}

因為我們的parentComponent一開始不存在,于是返回一個空對象。注意,這個空對象是重復使用的,不是每次返回一個新的空對象,這是一個很好的優化。

scheduleTopLevelUpdate是將用戶的傳參封裝成一個update對象, update對象有partialState對象,它就是相當于React15中 的setState的第一個state傳參。但現在partialState中竟然把children放進去了。

//by 司徒正美, 加群:370262116 一起研究React與anujs

function scheduleTopLevelUpdate(current, element, callback) {
    // // newRoot.fiber, children, callback

    callback = callback === undefined ? null : callback;
    var expirationTime = void 0;
    // Check if the top-level element is an async wrapper component. If so,
    // treat updates to the root as async. This is a bit weird but lets us
    // avoid a separate `renderAsync` API.
    if (enableAsyncSubtreeAPI && element != null && element.type != null && element.type.prototype != null && element.type.prototype.unstable_isAsyncReactComponent === true) {
      expirationTime = computeAsyncExpiration();
    } else {
      expirationTime = computeExpirationForFiber(current);//計算過時時間
    }

    var update = {
      expirationTime: expirationTime,//過時時間
      partialState: { element: element },//!!!!神奇
      callback: callback,
      isReplace: false,
      isForced: false,
      nextCallback: null,
      next: null
    };
    insertUpdateIntoFiber(current, update);//創建一個列隊
    scheduleWork(current, expirationTime);//執行列隊
  }

列隊是一個鏈表

//by 司徒正美, 加群:370262116 一起研究React與anujs
// https://github.com/RubyLouvre/anu 歡迎加star

function insertUpdateIntoFiber(fiber, update) {
  // We"ll have at least one and at most two distinct update queues.
  var alternateFiber = fiber.alternate;
  var queue1 = fiber.updateQueue;
  if (queue1 === null) {
    // TODO: We don"t know what the base state will be until we begin work.
    // It depends on which fiber is the next current. Initialize with an empty
    // base state, then set to the memoizedState when rendering. Not super
    // happy with this approach.
    queue1 = fiber.updateQueue = createUpdateQueue(null);
  }

  var queue2 = void 0;
  if (alternateFiber !== null) {
    queue2 = alternateFiber.updateQueue;
    if (queue2 === null) {
      queue2 = alternateFiber.updateQueue = createUpdateQueue(null);
    }
  } else {
    queue2 = null;
  }
  queue2 = queue2 !== queue1 ? queue2 : null;

  // If there"s only one queue, add the update to that queue and exit.
  if (queue2 === null) {
    insertUpdateIntoQueue(queue1, update);
    return;
  }

  // If either queue is empty, we need to add to both queues.
  if (queue1.last === null || queue2.last === null) {
    insertUpdateIntoQueue(queue1, update);
    insertUpdateIntoQueue(queue2, update);
    return;
  }

  // If both lists are not empty, the last update is the same for both lists
  // because of structural sharing. So, we should only append to one of
  // the lists.
  insertUpdateIntoQueue(queue1, update);
  // But we still need to update the `last` pointer of queue2.
  queue2.last = update;
}

function insertUpdateIntoQueue(queue, update) {
  // Append the update to the end of the list.
  if (queue.last === null) {
    // Queue is empty
    queue.first = queue.last = update;
  } else {
    queue.last.next = update;
    queue.last = update;
  }
  if (queue.expirationTime === NoWork || queue.expirationTime > update.expirationTime) {
    queue.expirationTime = update.expirationTime;
  }
}

scheduleWork是執行虛擬DOM(fiber樹)的更新。 scheduleWork,requestWork, performWork是三部曲。

//by 司徒正美, 加群:370262116 一起研究React與anujs

function scheduleWork(fiber, expirationTime) {
    return scheduleWorkImpl(fiber, expirationTime, false);
  }

  function checkRootNeedsClearing(root, fiber, expirationTime) {
    if (!isWorking && root === nextRoot && expirationTime < nextRenderExpirationTime) {
      // Restart the root from the top.
      if (nextUnitOfWork !== null) {
        // This is an interruption. (Used for performance tracking.)
        interruptedBy = fiber;
      }
      nextRoot = null;
      nextUnitOfWork = null;
      nextRenderExpirationTime = NoWork;
    }
  }

  function scheduleWorkImpl(fiber, expirationTime, isErrorRecovery) {
    recordScheduleUpdate();


    var node = fiber;
    while (node !== null) {
      // Walk the parent path to the root and update each node"s
      // expiration time.
      if (node.expirationTime === NoWork || node.expirationTime > expirationTime) {
        node.expirationTime = expirationTime;
      }
      if (node.alternate !== null) {
        if (node.alternate.expirationTime === NoWork || node.alternate.expirationTime > expirationTime) {
          node.alternate.expirationTime = expirationTime;
        }
      }
      if (node["return"] === null) {
        if (node.tag === HostRoot) {
          var root = node.stateNode;

          checkRootNeedsClearing(root, fiber, expirationTime);
          requestWork(root, expirationTime);
          checkRootNeedsClearing(root, fiber, expirationTime);
        } else {

          return;
        }
      }
      node = node["return"];
    }
  }


function requestWork(root, expirationTime) {
    if (nestedUpdateCount > NESTED_UPDATE_LIMIT) {
      invariant_1(false, "Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.");
    }

    // Add the root to the schedule.
    // Check if this root is already part of the schedule.
    if (root.nextScheduledRoot === null) {
      // This root is not already scheduled. Add it.
      root.remainingExpirationTime = expirationTime;
      if (lastScheduledRoot === null) {
        firstScheduledRoot = lastScheduledRoot = root;
        root.nextScheduledRoot = root;
      } else {
        lastScheduledRoot.nextScheduledRoot = root;
        lastScheduledRoot = root;
        lastScheduledRoot.nextScheduledRoot = firstScheduledRoot;
      }
    } else {
      // This root is already scheduled, but its priority may have increased.
      var remainingExpirationTime = root.remainingExpirationTime;
      if (remainingExpirationTime === NoWork || expirationTime < remainingExpirationTime) {
        // Update the priority.
        root.remainingExpirationTime = expirationTime;
      }
    }

    if (isRendering) {
      // Prevent reentrancy. Remaining work will be scheduled at the end of
      // the currently rendering batch.
      return;
    }

    if (isBatchingUpdates) {
      // Flush work at the end of the batch.
      if (isUnbatchingUpdates) {
        // unless we"re inside unbatchedUpdates, in which case we should
        // flush it now.
        nextFlushedRoot = root;
        nextFlushedExpirationTime = Sync;
        performWorkOnRoot(nextFlushedRoot, nextFlushedExpirationTime);
      }
      return;
    }

    // TODO: Get rid of Sync and use current time?
    if (expirationTime === Sync) {
      performWork(Sync, null);
    } else {
      scheduleCallbackWithExpiration(expirationTime);
    }
  }

 function performWork(minExpirationTime, dl) {
    deadline = dl;

    // Keep working on roots until there"s no more work, or until the we reach
    // the deadline.
    findHighestPriorityRoot();

    if (enableUserTimingAPI && deadline !== null) {
      var didExpire = nextFlushedExpirationTime < recalculateCurrentTime();
      stopRequestCallbackTimer(didExpire);
    }

    while (nextFlushedRoot !== null && nextFlushedExpirationTime !== NoWork && (minExpirationTime === NoWork || nextFlushedExpirationTime <= minExpirationTime) && !deadlineDidExpire) {
      performWorkOnRoot(nextFlushedRoot, nextFlushedExpirationTime);
      // Find the next highest priority work.
      findHighestPriorityRoot();
    }

    // We"re done flushing work. Either we ran out of time in this callback,
    // or there"s no more work left with sufficient priority.

    // If we"re inside a callback, set this to false since we just completed it.
    if (deadline !== null) {
      callbackExpirationTime = NoWork;
      callbackID = -1;
    }
    // If there"s work left over, schedule a new callback.
    if (nextFlushedExpirationTime !== NoWork) {
      scheduleCallbackWithExpiration(nextFlushedExpirationTime);
    }

    // Clean-up.
    deadline = null;
    deadlineDidExpire = false;
    nestedUpdateCount = 0;

    if (hasUnhandledError) {
      var _error4 = unhandledError;
      unhandledError = null;
      hasUnhandledError = false;
      throw _error4;
    }
  }

function performWorkOnRoot(root, expirationTime) {
    !!isRendering ? invariant_1(false, "performWorkOnRoot was called recursively. This error is likely caused by a bug in React. Please file an issue.") : void 0;

    isRendering = true;

    // Check if this is async work or sync/expired work.
    // TODO: Pass current time as argument to renderRoot, commitRoot
    if (expirationTime <= recalculateCurrentTime()) {
      // Flush sync work.
      var finishedWork = root.finishedWork;
      if (finishedWork !== null) {
        // This root is already complete. We can commit it.
        root.finishedWork = null;
        root.remainingExpirationTime = commitRoot(finishedWork);
      } else {
        root.finishedWork = null;
        finishedWork = renderRoot(root, expirationTime);
        if (finishedWork !== null) {
          // We"ve completed the root. Commit it.
          root.remainingExpirationTime = commitRoot(finishedWork);
        }
      }
    } else {
      // Flush async work.
      var _finishedWork = root.finishedWork;
      if (_finishedWork !== null) {
        // This root is already complete. We can commit it.
        root.finishedWork = null;
        root.remainingExpirationTime = commitRoot(_finishedWork);
      } else {
        root.finishedWork = null;
        _finishedWork = renderRoot(root, expirationTime);
        if (_finishedWork !== null) {
          // We"ve completed the root. Check the deadline one more time
          // before committing.
          if (!shouldYield()) {
            // Still time left. Commit the root.
            root.remainingExpirationTime = commitRoot(_finishedWork);
          } else {
            // There"s no time left. Mark this root as complete. We"ll come
            // back and commit it later.
            root.finishedWork = _finishedWork;
          }
        }
      }
    }

   isRendering = false;
}
//用于調整渲染順序,高優先級的組件先執行
function findHighestPriorityRoot() {
    var highestPriorityWork = NoWork;
    var highestPriorityRoot = null;

    if (lastScheduledRoot !== null) {
      var previousScheduledRoot = lastScheduledRoot;
      var root = firstScheduledRoot;
      while (root !== null) {
        var remainingExpirationTime = root.remainingExpirationTime;
        if (remainingExpirationTime === NoWork) {
          // This root no longer has work. Remove it from the scheduler.

          // TODO: This check is redudant, but Flow is confused by the branch
          // below where we set lastScheduledRoot to null, even though we break
          // from the loop right after.
          !(previousScheduledRoot !== null && lastScheduledRoot !== null) ? invariant_1(false, "Should have a previous and last root. This error is likely caused by a bug in React. Please file an issue.") : void 0;
          if (root === root.nextScheduledRoot) {
            // This is the only root in the list.
            root.nextScheduledRoot = null;
            firstScheduledRoot = lastScheduledRoot = null;
            break;
          } else if (root === firstScheduledRoot) {
            // This is the first root in the list.
            var next = root.nextScheduledRoot;
            firstScheduledRoot = next;
            lastScheduledRoot.nextScheduledRoot = next;
            root.nextScheduledRoot = null;
          } else if (root === lastScheduledRoot) {
            // This is the last root in the list.
            lastScheduledRoot = previousScheduledRoot;
            lastScheduledRoot.nextScheduledRoot = firstScheduledRoot;
            root.nextScheduledRoot = null;
            break;
          } else {
            previousScheduledRoot.nextScheduledRoot = root.nextScheduledRoot;
            root.nextScheduledRoot = null;
          }
          root = previousScheduledRoot.nextScheduledRoot;
        } else {
          if (highestPriorityWork === NoWork || remainingExpirationTime < highestPriorityWork) {
            // Update the priority, if it"s higher
            highestPriorityWork = remainingExpirationTime;
            highestPriorityRoot = root;
          }
          if (root === lastScheduledRoot) {
            break;
          }
          previousScheduledRoot = root;
          root = root.nextScheduledRoot;
        }
      }
    }

    // If the next root is the same as the previous root, this is a nested
    // update. To prevent an infinite loop, increment the nested update count.
    var previousFlushedRoot = nextFlushedRoot;
    if (previousFlushedRoot !== null && previousFlushedRoot === highestPriorityRoot) {
      nestedUpdateCount++;
    } else {
      // Reset whenever we switch roots.
      nestedUpdateCount = 0;
    }
    nextFlushedRoot = highestPriorityRoot;
    nextFlushedExpirationTime = highestPriorityWork;
  }

這只是一部分更新邏輯, 簡直沒完沒了,下次繼續,添上流程圖,回憶一下本文學到的東西

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/107189.html

相關文章

  • React16.2fiber架構(2)

    摘要:為了幫助理解,我們繼續加日志司徒正美,加群一起研究與只要收到更新對象,就會被調度程序調用。渲染器在將來的某個時刻調用。導步肯定為歡迎加繼續略也是怒長,代碼的特點是許多巨型類,巨型方法,有之遺風。 insertUpdateIntoFiber 會根據fiber的狀態創建一個或兩個列隊對象,對象是長成這樣的 //by 司徒正美, 加群:370262116 一起研究React與anujs //...

    Caicloud 評論0 收藏0
  • 精讀《React16 新特性》

    摘要:引言于發布版本,時至今日已更新到,且引入了大量的令人振奮的新特性,本文章將帶領大家根據更新的時間脈絡了解的新特性。其作用是根據傳遞的來更新。新增等指針事件。 1 引言 于 2017.09.26 Facebook 發布 React v16.0 版本,時至今日已更新到 React v16.6,且引入了大量的令人振奮的新特性,本文章將帶領大家根據 React 更新的時間脈絡了解 React1...

    Nosee 評論0 收藏0
  • 淺談React Fiber

    摘要:因為版本將真正廢棄這三生命周期到目前為止,的渲染機制遵循同步渲染首次渲染,更新時更新時卸載時期間每個周期函數各司其職,輸入輸出都是可預測,一路下來很順暢。通過進一步觀察可以發現,預廢棄的三個生命周期函數都發生在虛擬的構建期間,也就是之前。 showImg(https://segmentfault.com/img/bVbweoj?w=559&h=300); 背景 前段時間準備前端招聘事項...

    izhuhaodev 評論0 收藏0
  • React系列——React Fiber 架構介紹資料匯總(翻譯+中文資料)

    摘要:它的主體特征是增量渲染能夠將渲染工作分割成塊,并將其分散到多個幀中。實際上,這樣做可能會造成浪費,導致幀丟失并降低用戶體驗。當一個函數被執行時,一個新的堆棧框架被添加到堆棧中。該堆棧框表示由該函數執行的工作。 原文 react-fiber-architecture 介紹 React Fibre是React核心算法正在進行的重新實現。它是React團隊兩年多的研究成果。 React ...

    taohonghui 評論0 收藏0

發表評論

0條評論

lansheng228

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<