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月前
|
移动开发 JavaScript 前端开发
VUE实现一个列表清单【props 父子组件通信、slot插槽的使用、全局自定义指令的封装、$nextTick解决异步DOM更新、巧用v-model简化父子组件之间的通信、触发事件的事件源event】
VUE实现一个列表清单【props 父子组件通信、slot插槽的使用、全局自定义指令的封装、$nextTick解决异步DOM更新、巧用v-model简化父子组件之间的通信、触发事件的事件源event】
49 0
|
7月前
|
存储 前端开发 JavaScript
在回调函数中重新渲染React组件
在React中,重新渲染组件可通过`forceUpdate()`或`ReactDOM.render()`实现。方法一是使用`forceUpdate`强制无状态组件更新;方法二是通过重新创建根组件实例适用于有状态组件。这些示例基于Webpack和Babel的模块热替换配置。根据项目需求和React版本,还可以结合React-Router或Redux等库选择合适的方法。
|
前端开发
react +Antd Cascader级联选择使用接口数据渲染
react +Antd Cascader级联选择使用接口数据渲染
193 0
|
前端开发 JavaScript
React render 的原理和触发时机
React render 的原理和触发时机
170 0
|
人工智能 自然语言处理 JavaScript
$nextTick与原生js先渲染dom再执行函数的方式
$nextTick与原生js先渲染dom再执行函数的方式
153 0
|
JavaScript
vue组件与iframe通信,防止多次触发messag事件
vue组件与iframe通信,防止多次触发messag事件
368 0
|
前端开发 JavaScript
【Render】如何将数据渲染到页面?渲染数据的流程
【Render】如何将数据渲染到页面?渲染数据的流程
202 0
|
缓存 JavaScript 前端开发
vue3预渲染和服务端渲染(同构)示例讲解
vue3预渲染和服务端渲染(同构)示例讲解
560 0
vue3预渲染和服务端渲染(同构)示例讲解
|
数据采集 前端开发 JavaScript
一起了解基于React的服务端渲染&同构
一起了解基于React的服务端渲染&同构