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树图解函数

相关文章
相关标签/搜索