React源碼學習筆記---render流程(1)

ReactDOM.render

render(
    element: React$Element<any>,
    container: DOMContainer,
    callback: ?Function,
  ) {
    // 注意下 forceHydrate 參數(也就是第四個參數),爲 true 時是服務端渲染
    // 調用 render 函數的話這個值永遠爲 false,調用 hydrate 函數的話這個值會爲 true
    return legacyRenderSubtreeIntoContainer(
      null,
      element,
      container,
      false,
      callback,
    );
  }
複製代碼

接下來是legacyRenderSubtreeIntoContainer

沒有root節點,建立一個新的react

function legacyRenderSubtreeIntoContainer(
  parentComponent: ?React$Component<any, any>,
  children: ReactNodeList,
  container: DOMContainer,
  forceHydrate: boolean,
  callback: ?Function,
) {

  // 一開始進來 container 上是確定沒有這個屬性的
  let root: Root = (container._reactRootContainer: any);
  // 沒有 root 會執行 if 中的操做
  if (!root) {
    // Initial mount
    // 建立一個 root 出來,類型是 ReactRoot,而且掛載到container._reactRootContainer上
    root = container._reactRootContainer = legacyCreateRootFromDOMContainer(
      container,
      forceHydrate,
    );
  }
}
複製代碼

接下來是legacyCreateRootFromDOMContainer

返回ReactRoot的實例bash

function legacyCreateRootFromDOMContainer(
  container: DOMContainer,
  forceHydrate: boolean,
): Root {
  const shouldHydrate =
    forceHydrate || shouldHydrateDueToLegacyHeuristic(container);
  // forceHydrate爲false,因此進if的判斷
  if (!shouldHydrate) {
    let warned = false;
    let rootSibling;
    // container 內部若是有元素的話,就所有清掉
    // 可是通常來講咱們都是這樣寫 container 的: <div id='root'></div>
    // 因此說 container 內部不要寫任何的節點,一是會被清掉,二是還要進行 DOM 操做,可能還會涉及到重繪迴流等等
    while ((rootSibling = container.lastChild)) {
      container.removeChild(rootSibling);
    }
  }
  // Legacy roots are not async by default.
  // 對於 Root 來講不須要異步
  const isConcurrent = false;
  return new ReactRoot(container, isConcurrent, shouldHydrate);
}
複製代碼

ReactRoot

返回建立FiberRoot方法,而且ReactRoot實例經過_internalRoot關聯數據結構

function ReactRoot(
  container: DOMContainer,
  isConcurrent: boolean,
  hydrate: boolean,
) {
  // 這個 root 指的是 FiberRoot
  const root = createContainer(container, isConcurrent, hydrate);
  this._internalRoot = root;
}

function createContainer(
  containerInfo: Container,
  isConcurrent: boolean,
  hydrate: boolean,
): OpaqueRoot {
  return createFiberRoot(containerInfo, isConcurrent, hydrate);
}
複製代碼

createFiberRoot

建立FiberRoot節點和RootFiber,他們之間相互引用,對於 FiberRoot 對象來講,咱們如今只須要了解兩個屬性,分別是 containerInfo 及 current。前者表明着容器信息,也就是咱們的 document.querySelector('#root');後者指向 RootFiber架構

對於 RootFiber 對象來講,咱們須要瞭解的屬性稍微多點.return、child、sibling 這三個屬性很重要,它們是構成 fiber 樹的主體數據結構。fiber 樹實際上是一個單鏈表樹結構,return 及 child 分別對應着樹的父子節點,而且父節點只有一個 child 指向它的第一個子節點,即使是父節點有好多個子節點。那麼多個子節點如何鏈接起來呢?答案是 sibling,每一個子節點都有一個 sibling 屬性指向着下一個子節點,都有一個 return 屬性指向着父節點。異步

最後是 alternate 屬性。其實在一個 React 應用中,一般來講都有兩個 fiebr 樹,一個叫作 old tree,另外一個叫作 workInProgress tree。前者對應着已經渲染好的 DOM 樹,後者是正在執行更新中的 fiber tree,還能便於中斷後恢復。兩棵樹的節點互相引用,便於共享一些內部的屬性,減小內存的開銷(double buffering)。async

function createFiberRoot(
  containerInfo: any,
  isConcurrent: boolean,
  hydrate: boolean,
): FiberRoot {
  // FiberRootNode 內部建立了不少屬性
  const root: FiberRoot = (new FiberRootNode(containerInfo, hydrate): any);

  // Cyclic construction. This cheats the type system right now because
  // stateNode is any.
  // 建立一個 root fiber,這也是 React 16 中的核心架構了
  // fiber 其實也會組成一個樹結構,內部使用了單鏈表樹結構,每一個節點及組件都會對應一個 fiber
  // FiberRoot 和 Root Fiber 會互相引用

  // 另外若是你有 React 寫的項目的話,能夠經過如下代碼找到 Fiber Root,它對應着容器
  // document.querySelector('#root')._reactRootContainer._internalRoot
  const uninitializedFiber = createHostRootFiber(isConcurrent);
  root.current = uninitializedFiber;
  uninitializedFiber.stateNode = root;

  return root;
}
複製代碼

Fiber樹圖解函數

相關文章
相關標籤/搜索