剖析 React 源码:render 流程(一)

栏目: 服务器 · 发布时间: 5年前

内容简介:这是我的剖析 React 源码的第二篇文章,如果你没有阅读过之前的文章,请务必先阅读一下现在请大家打开想必大家在写 React 项目的时候都写过类似的代码

这是我的剖析 React 源码的第二篇文章,如果你没有阅读过之前的文章,请务必先阅读一下 第一篇文章 中提到的一些注意事项,能帮助你更好地阅读源码。

现在请大家打开 我的代码 并定位到 react-dom 文件夹下的 src 中的 ReactDOM.js 文件,今天的内容会从这里开始。

render

想必大家在写 React 项目的时候都写过类似的代码

ReactDOM.render(<APP />, document.getElementById('root')
复制代码

这句代码告诉了 React 应用我们想在容器中渲染出一个组件,这通常也是一个 React 应用的入口代码,接下来我们就来梳理整个 render 的流程,并且会分为几篇文章来讲解,因为流程实在太长了。

首先请大家先定位到 ReactDOM.js 文件的第 702 行代码,开始今天的旅程。

剖析 React 源码:render 流程(一)

这部分代码其实没啥好说的,唯一需要注意的是在调用 legacyRenderSubtreeIntoContainer 函数时写死了第四个参数 forceHydratefalse 。这个参数为 true 时表明了是服务端渲染,因为我们分析的是客户端渲染,因此后面有关这部分的内容也不会再展开。

接下来进入 legacyRenderSubtreeIntoContainer 函数中,这部分代码分为两块来讲。第一部分是没有 root 之前我们首先需要创建一个 root (对应这篇文章),第二部分是有 root 之后的渲染流程(对应接下来的文章)。

剖析 React 源码:render 流程(一)

一开始进来函数的时候肯定是没有 root 的,因此我们需要去创建一个 root ,大家可以发现这个 root 对象同样也被挂载在了 container._reactRootContainer 上,也就是我们的 DOM 容器上。 如果你手边有 React 项目的话,在控制台键入如下代码就可以看到这个 root 对象了。

document.querySelector('#root')._reactRootContainer
复制代码
剖析 React 源码:render 流程(一)

大家可以看到 rootReactRoot 构造函数构造出来的,并且内部有一个 _internalRoot 对象,这个对象是本文接下来要重点介绍的 fiber 对象,接下来我们就来一窥究竟吧。

剖析 React 源码:render 流程(一)

首先还是和上文中提到的 forceHydrate 属性相关的内容,不需要管这部分,反正 shouldHydrate 肯定为 false

接下来是将容器内部的节点全部移除,一般来说我们都是这样写一个容器的的

<div id='root'></div>
复制代码

这样的形式肯定就不需要去移除子节点了,这也侧面说明了一点那就是容器内部不要含有任何的子节点。一是肯定会被移除掉,二来还要进行 DOM 操作,可能还会涉及到重绘回流等等。

最后就是创建了一个 ReactRoot 对象并返回。接下来的内容中我们会看到好几个 root ,可能会有点绕。

剖析 React 源码:render 流程(一)

ReactRoot 构造函数内部就进行了一步操作,那就是创建了一个 FiberRoot 对象,并挂载到了 _internalRoot 上。 和 DOM 树一样, fiber 也会构建出一个树结构(每个 DOM 节点一定对应着一个 fiber 对象), FiberRoot 就是整个 fiber 树的根节点 ,接下来的内容里我们将学习到关于 fiber 相关的内容。这里提及一点, fiber 和 Fiber 是两个不一样的东西,前者代表着数据结构,后者代表着新的架构。

剖析 React 源码:render 流程(一)

createFiberRoot 函数内部,分别创建了两个 root ,一个 root 叫做 FiberRoot ,另一个 root 叫做 RootFiber ,并且它们两者还是相互引用的。

这两个对象内部拥有着数十个属性,现在我们没有必要一一去了解它们各自有什么用处,在当下只需要了解少部分属性即可,其他的属性我们会在以后的文章中了解到它们的用处。

对于 FiberRoot 对象来说,我们现在只需要了解两个属性,分别是 containerInfocurrent 。前者代表着容器信息,也就是我们的 document.querySelector('#root') ;后者指向 RootFiber

对于 RootFiber 对象来说,我们需要了解的属性稍微多点

function FiberNode(
  tag: WorkTag,
  pendingProps: mixed,
  key: null | string,
  mode: TypeOfMode,
) {
  this.stateNode = null;
  this.return = null;
  this.child = null;
  this.sibling = null;
  this.effectTag = NoEffect;
  this.alternate = null;
}
复制代码

stateNode 上文中已经讲过了,这里就不再赘述。

returnchildsibling 这三个属性很重要,它们是构成 fiber 树的主体数据结构。 fiber 树其实是一个单链表树结构, returnchild 分别对应着树的父子节点,并且父节点只有一个 child 指向它的第一个子节点,即便是父节点有好多个子节点。那么多个子节点如何连接起来呢?答案是 sibling ,每个子节点都有一个 sibling 属性指向着下一个子节点,都有一个 return 属性指向着父节点。这么说可能有点绕,我们通过图来了解一下这个 fiber 树的结构。

const APP = () => (
    <div>
        <span></span>
        <span></span>
    </div>
)
ReactDom.render(<APP/>, document.querySelector('#root'))
复制代码

假如说我们需要渲染出以上组件,那么它们对应的 fiber 树应该长这样

剖析 React 源码:render 流程(一)

从图中我们可以看到,每个组件或者 DOM 节点都会对应着一个 fiber 对象。另外你手边有 React 项目的话,也可以在控制台输入如下代码,查看 fiber 树的整个结构。

// 对应着 FiberRoot
const fiber = document.querySelector('#root')._reactRootContainer._internalRoot
复制代码

另外两个属性在本文中虽然用不上,但是看源码的时候笔者觉得很有意思,就打算拿出来说一下。

在说 effectTag 之前,我们先来了解下啥是 effect ,简单来说就是 DOM 的一些操作,比如增删改,那么 effectTag 就是来记录所有的 effect 的,但是这个记录是通过位运算来实现的, 这里effectTag 相关的二进制内容。

如果我们想新增一个 effect 的话,可以这样写 effectTag |= Update ;如果我们想删除一个 effect 的话,可以这样写 effectTag &= ~Update

最后是 alternate 属性。其实在一个 React 应用中,通常来说都有两个 fiebr 树,一个叫做 old tree,另一个叫做 workInProgress tree。前者对应着已经渲染好的 DOM 树,后者是正在执行更新中的 fiber tree,还能便于中断后恢复。两棵树的节点互相引用,便于共享一些内部的属性,减少内存的开销。毕竟前文说过每个组件或 DOM 都会对应着一个 fiber 对象,应用很大的话组成的 fiber 树也会很大,如果两棵树都是各自把一些相同的属性创建一遍的话,会损失不少的内存空间及性能。

当更新结束以后,workInProgress tree 会将 old tree 替换掉,这种做法称之为 double buffering,这也是性能优化里的一种做法,有兴趣的同学可以自行查找资料。

总结

以上就是本文的全部内容了,最后通过一张流程图总结一下这篇文章的内容。

剖析 React 源码:render 流程(一)

最后

阅读源码是一个很枯燥的过程,但是收益也是巨大的。如果你在阅读的过程中有任何的问题,都欢迎你在评论区与我交流。

另外写这系列是个很耗时的工程,需要维护代码注释,还得把文章写得尽量让读者看懂,最后还得配上画图,如果你觉得文章看着还行,就请不要吝啬你的点赞。

下一篇文章还是 render 流程相关的内容。

最后,觉得内容有帮助可以关注下我的公众号 「前端真好玩」咯,会有很多好东西等着你。

剖析 React 源码:render 流程(一)

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

白话大数据与机器学习

白话大数据与机器学习

高扬、卫峥、尹会生 / 机械工业出版社 / 2016-6 / 69

本书通俗易懂,有高中数学基础即可看懂,同时结合大量案例与漫画,将高度抽象的数学、算法与应用,与现实生活中的案例和事件一一做了关联,将源自生活的抽象还原出来,帮助读者理解后,又带领大家将这些抽象的规律与算法应用于实践,贴合读者需求。同时,本书不是割裂讲解大数据与机器学习的算法和应用,还讲解了其生态环境与关联内容,让读者更全面地知晓渊源与未来,是系统学习大数据与机器学习的不二之选: ·大数据产业......一起来看看 《白话大数据与机器学习》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具