ReactDOM的render和Fiber源碼解析

render()

ReactDOM.render(element, container[, callback])

在提供的 container 里渲染一個 React 元素,并返回對該組件的引用(或者針對無狀態(tài)組件返回 null)。

如果 React 元素之前已經(jīng)在 container 里渲染過,這將會對其執(zhí)行更新操作,并僅會在必要時改變 DOM 以映射最新的 React 元素。

如果提供了可選的回調(diào)函數(shù),該回調(diào)將在組件被渲染或更新之后被執(zhí)行。

注意:

ReactDOM.render() 會控制你傳入容器節(jié)點里的內(nèi)容。當首次調(diào)用時,容器節(jié)點里的所有 DOM 元素都會被替換,后續(xù)的調(diào)用則會使用 React 的 DOM 差分算法(DOM diffing algorithm)進行高效的更新。

ReactDOM.render() 不會修改容器節(jié)點(只會修改容器的子節(jié)點)。可以在不覆蓋現(xiàn)有子節(jié)點的情況下,將組件插入已有的 DOM 節(jié)點中。

ReactDOM.render() 目前會返回對根組件 ReactComponent 實例的引用。 但是,目前應該避免使用返回的引用,因為它是歷史遺留下來的內(nèi)容,而且在未來版本的 React 中,組件渲染在某些情況下可能會是異步的。 如果你真的需要獲得對根組件 ReactComponent 實例的引用,那么推薦為根元素添加 callback ref

使用 ReactDOM.render() 對服務端渲染容器進行 hydrate 操作的方式已經(jīng)被廢棄,并且會在 React 17 被移除。作為替代,請使用 hydrate()。

packages/react-dom/ReactDOM.js 源碼解析

時序圖.png
render(
    element,
    container,
    callback,
  ) {
    invariant(
      isValidContainer(container),
      'Target container is not a DOM element.',
    );
    if (__DEV__) {
      warningWithoutStack(
        !container._reactHasBeenPassedToCreateRootDEV,
        'You are calling ReactDOM.render() on a container that was previously ' +
          'passed to ReactDOM.%s(). This is not supported. ' +
          'Did you mean to call root.render(element)?',
        enableStableConcurrentModeAPIs ? 'createRoot' : 'unstable_createRoot',
      );
    }
    return legacyRenderSubtreeIntoContainer(
      null,
      element,
      container,
      false,
      callback,
    );
  }

ReactDOM.render方法中調(diào)用了legacyRenderSubtreeIntoContainer方法:

function legacyRenderSubtreeIntoContainer(
  parentComponent,
  children,
  container,
  forceHydrate,
  callback,
) {
  if (__DEV__) {
    topLevelUpdateWarnings(container);
    warnOnInvalidCallback(callback === undefined ? null : callback, 'render');
  }

  // TODO: Without `any` type, Flow says "Property cannot be accessed on any
  // member of intersection type." Whyyyyyy.
  let root = container._reactRootContainer;
  let fiberRoot;
  if (!root) {
    // Initial mount
    root = container._reactRootContainer = legacyCreateRootFromDOMContainer(
      container,
      forceHydrate,
    );
    fiberRoot = root._internalRoot;
    if (typeof callback === 'function') {
      const originalCallback = callback;
      callback = function() {
        const instance = getPublicRootInstance(fiberRoot);
        originalCallback.call(instance);
      };
    }
    // Initial mount should not be batched.
    unbatchedUpdates(() => {
      updateContainer(children, fiberRoot, parentComponent, callback);
    });
  } else {
    fiberRoot = root._internalRoot;
    if (typeof callback === 'function') {
      const originalCallback = callback;
      callback = function() {
        const instance = getPublicRootInstance(fiberRoot);
        originalCallback.call(instance);
      };
    }
    // Update
    updateContainer(children, fiberRoot, parentComponent, callback);
  }
  return getPublicRootInstance(fiberRoot);
}
  • 如果沒有container._reactRootContainer值,則說明是第一次掛載到該container下,這時候,react在執(zhí)行legacyCreateRootFromDOMContainer的會清空該dom下的子元素,并返回一個ReactSyncRoot類型的對象。并且第一次掛載的時候,shouldHydrate是為false的。
  • 經(jīng)過legacyCreateRootFromDOMContainer 之后,container的數(shù)據(jù)是這樣的:
container._reactRootContainer = ReactRoot (from legacyCreateRootFromDOMContainer);
container._reactRootContainer._internalRoot = FiberRoot
container._reactRootContainer._internalRoot.current = FiberNode (HostRoot)
container._reactRootContainer._internalRoot.current.stateNode = container._reactRootContainer._internalRoot = FiberRoot

FiberRoot和Fiber的數(shù)據(jù)結(jié)構(gòu):

function FiberRootNode(containerInfo, tag, hydrate) {
  this.tag = tag;
  this.current = null;
  this.containerInfo = containerInfo;
  this.pendingChildren = null;
  this.pingCache = null;
  this.finishedExpirationTime = NoWork;
  this.finishedWork = null;
  this.timeoutHandle = noTimeout;
  this.context = null;
  this.pendingContext = null;
  this.hydrate = hydrate;
  this.firstBatch = null;
  this.callbackNode = null;
  this.callbackExpirationTime = NoWork;
  this.firstPendingTime = NoWork;
  this.lastPendingTime = NoWork;
  this.pingTime = NoWork;

  if (enableSchedulerTracing) {
    this.interactionThreadID = unstable_getThreadID();
    this.memoizedInteractions = new Set();
    this.pendingInteractionMap = new Map();
  }
}

function FiberNode(
  tag,
  pendingProps,
  key,
  mode,
) {
  // Instance
  this.tag = tag;
  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;

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

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

  this.expirationTime = NoWork;
  this.childExpirationTime = NoWork;

  this.alternate = null;
}

接下去就是關(guān)鍵的updateContainer函數(shù)了。這個函數(shù)牽涉的面比較多,我們下一片再說。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

  • 40、React 什么是React?React 是一個用于構(gòu)建用戶界面的框架(采用的是MVC模式):集中處理VIE...
    萌妹撒閱讀 1,185評論 0 1
  • 原教程內(nèi)容詳見精益 React 學習指南,這只是我在學習過程中的一些閱讀筆記,個人覺得該教程講解深入淺出,比目前大...
    leonaxiong閱讀 2,944評論 1 18
  • 以下內(nèi)容是我在學習和研究React時,對React的特性、重點和注意事項的提取、精練和總結(jié),可以做為React特性...
    科研者閱讀 8,408評論 2 21
  • React基礎 React組件化編程 Create React App 創(chuàng)建React 前端工程 題外話題:頁面性...
    BeautifulHao閱讀 1,636評論 0 3
  • 有點急躁,感覺一大堆事情涌來,想做的事情太多
    冰淇淋很餓閱讀 204評論 1 0

友情鏈接更多精彩內(nèi)容