ReactDOM.render串联渲染链路(一)

简介: 本文主要讲ReactDOM.render串联渲染链路

网络异常,图片无法展示
|


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


  • 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》
目录
相关文章
vue3渲染函数(h函数)的变化
vue3渲染函数(h函数)的变化
|
5月前
|
缓存 前端开发
useMemo问题之提高组件第一次渲染的速度如何解决
useMemo问题之提高组件第一次渲染的速度如何解决
|
5月前
|
前端开发 JavaScript 算法
react【框架原理详解】JSX 的本质、SyntheticEvent 合成事件机制、组件渲染过程、组件更新过程
react【框架原理详解】JSX 的本质、SyntheticEvent 合成事件机制、组件渲染过程、组件更新过程
74 0
|
7月前
|
JavaScript 开发者
Teleport传送:使用Vue的Teleport进行跨DOM结构渲染
【4月更文挑战第24天】Vue.js的`<teleport>`组件用于跨DOM结构渲染,解决组件视觉呈现跨越父组件DOM的问题。它允许子组件内容传送到DOM的任意位置,如示例中将模态框移到`modal-container`元素。通过`target`属性指定目标元素,结合`v-if`控制显示,实现灵活的UI布局和交互。在适当场景下使用`<teleport>`能优化复杂应用的结构。
85 3
|
前端开发 JavaScript Java
react中实现组件之间的转场动画
react中实现组件之间的转场动画
263 0
|
7月前
|
JavaScript 前端开发 算法
Vue.js的单向数据流:让你的应用更清晰、更可控
Vue.js的单向数据流:让你的应用更清晰、更可控
|
缓存 前端开发 JavaScript
如何减少React中无关组件的重渲染
你是否同我一样,总是会遇到一些莫名其妙的渲染问题,有时为了解决bug,需要耗费相当气力来debug呢?快来一起学习下react re-render 这些小技巧吧,或许能帮你减少组件树中无关组件的重渲染及重挂载,可以提升性能,同时也能提高用户体验哟。 案例代码:https://github.com/buzingar/re-render-demos
2325 5
|
前端开发
react +Antd Cascader级联选择使用接口数据渲染
react +Antd Cascader级联选择使用接口数据渲染
191 0
|
前端开发 JavaScript
React render 的原理和触发时机
React render 的原理和触发时机
168 0
|
前端开发 JavaScript
【Render】如何将数据渲染到页面?渲染数据的流程
【Render】如何将数据渲染到页面?渲染数据的流程
199 0