乐趣区

关于程序员:ReactDOMrender串联渲染链路一

学习串联渲染链路你能学到什么?


  • React 16 在所有状况下都是异步渲染的吗?
  • Fiber 架构中的“可中断”“可复原”到底是如何实现的?
  • Fiber 树和传统虚构 DOM 树有何不同?
  • 优先级调度又是如何实现的?

ReactDOM.render 调用栈


ReactDOM.render 调用栈大抵能够拆分成如下三个阶段:

  • 初始化阶段
  • render 阶段
  • commit 阶段

初始化阶段


实现 Fiber 树中根本实体的创立,哪什么是 Fiber,它是干啥的,根本实体又是什么?

Fiber 是什么?

Fiber 是对 React 外围算法的重构。

Fiber 的作用?
  • 把可中断的工作拆分小工作
  • 对正在操作的工作调整优先秩序、重做、复用
  • 在父子工作之间切换,以反对 React 执行过程中的布局刷新
  • 反对 render 返回多个元素
根本实体是什么?

技根底实体想要理解他,来看一段源码

function legacyRenderSubtreeIntoContainer(parentComponent, children, container, forceHydrate, callback) {
  // container 对应的是咱们传入的实在 DOM 对象
  var root = container._reactRootContainer;
  // 初始化 fiberRoot 对象
  var fiberRoot;
  // DOM 对象自身不存在 _reactRootContainer 属性,因而 root 为空
  if (!root) {
    // 若 root 为空,则初始化 _reactRootContainer,并将其值赋值给 root
    root = container._reactRootContainer = legacyCreateRootFromDOMContainer(container, forceHydrate);
    // legacyCreateRootFromDOMContainer 创立出的对象会有一个 _internalRoot 属性,将其赋值给 fiberRoot
    fiberRoot = root._internalRoot;
    // 这里解决的是 ReactDOM.render 入参中的回调函数,你理解即可
    if (typeof callback === 'function') {
      var originalCallback = callback;
      callback = function () {var instance = getPublicRootInstance(fiberRoot);
        originalCallback.call(instance);
      };
    } // Initial mount should not be batched.
    // 进入 unbatchedUpdates 办法
    unbatchedUpdates(function () {updateContainer(children, fiberRoot, parentComponent, callback);
    });
  } else {
    // else 逻辑解决的是非首次渲染的状况(即更新),其逻辑除了跳过了初始化工作,与楼上基本一致
    fiberRoot = root._internalRoot;
    if (typeof callback === 'function') {
      var _originalCallback = callback;
      callback = function () {var instance = getPublicRootInstance(fiberRoot);
        _originalCallback.call(instance);
      };
    } // Update
    updateContainer(children, fiberRoot, parentComponent, callback);
  }
  return getPublicRootInstance(fiberRoot);
}

在这段源码中次要的操作就是赋值 fiberRoot 对象,这个对象是通过 root._internalRoot 赋值,实质上这个对象是一个 FiberRootNode 对象,其中蕴含一个 current 对象,这个对象是 FiberNode 实例。而 FiberNode,正是 Fiber 节点对应的对象类型。current 对象是一个 Fiber 节点,不仅如此,它还是以后 Fiber 树的头部节点。其中,fiberRoot 的关联对象是实在 DOM 的容器节点;而 rootFiber 则作为虚构 DOM 的根节点存在。这两个节点,将是后续整棵 Fiber 树构建的终点。

整个初始化的工作过程都是在为后续的 render 阶段做筹备。当初,咱们的 Fiber Tree 还处在只有根节点的起始状态。接下来,咱们就要进入到最最要害的 render 阶段里去,一起去看看这棵树是怎么一点点开枝散叶的吧!

render 阶段


scheduleUpdateOnFiber 办法的作用是调度更新,在由 ReactDOM.render 发动的首屏渲染这个场景下,它触发的就是 performSyncWorkOnRoot。performSyncWorkOnRoot 开启的正是咱们反复强调的 render 阶段;finishSyncRoot 标记着 render 办法的完结。在这个过程中,交叉了大量了 beginWork、completeWork 调用 (这两个办法串联起来就是一个模仿递归的过程)。这个两个办法就是 render 的工作内容。

在 React15 中和谐是一个递归的过程。在 Fiber 架构下,尽管不依赖递归,然而 ReactDOM.render 模式下,他整体是一个同步过程,是一个优先深度遍历。在这个过程中 beginWork 是负责创立 Fiber 节点,completeWork 负责将 Fiber 节点映射为实在 DOM。

接下来来看看这个过程有那些比拟重要的调用

performSyncWorkOnRoot:开始 render 阶段

renderRootSync:筹备工作

prepareFreshStack:重置一个新的堆栈环境


这里比拟重要的是调用了 createWorkInProgress 这个函数,咱们来看看这个函数干了什么

createWorkInProgress

workLoopSync:通过 while 循环反复判断 workInProgress 是否为空,并在不为空的状况下针对它执行 performUnitOfWork 函数。

performUnitOfWork:触发对 beginWork 的调用。performUnitOfWork,其次要工作是“通过调用 beginWork,来实现新 Fiber 节点的创立”;它还有一个主要工作,就是把新创建的这个 Fiber 节点的值更新到 workInProgress 变量里去。

beginWork:创立 Fiber 节点

beginWork 的外围逻辑是依据 fiber 节点(workInProgress)的 tag 属性的不同,调用不同的节点创立函数。在 beginWork 前面还有很多逻辑,感兴趣的同学能够深挖,借鉴网上的一张图如下:

Fiber 节点间是如何连贯

不同的 Fiber 节点之间,将通过 child、return、sibling 这 3 个属性建设关系,其中 child、return 记录的是父子节点关系,而 sibling 记录的则是兄弟节点关系。FiberNode 实例中,return 指向的是以后 Fiber 节点的父节点,而 sibling 指向的是以后节点的第 1 个兄弟节点。

后续

下一章持续为大家来分享 ReactDOM.render 串联渲染链路
感激修言大神的《深入浅出搞定 React》
退出移动版