前言

本次React源码参考版本为17.0.3

React架构前世今生

查阅文档了解到, React@16.x是个分水岭。

React@15及之前

在16之前,React架构大致可以分为两层:

  • Reconciler: 主要职责是对比查找更新前后的变化的组件;
  • Renderer: 主要职责是基于变化渲染页面;

但是React团队意识到这样的架构有致命问题: 因为在React15中,组件的更新是基于递归查找实现的,这样一旦开始递归,是没有办法中断的,如果组件层级很深,就会出现性能问题,导致页面卡顿。

React@16及之后

为了解决这样的问题,React团队在React@16进行了重构,引入了新的架构模型:

  • Reconciler: 主要职责是对比查找更新前后的变化的组件;
  • Renderer: 主要职责是基于变化渲染页面;
  • Scheduler: 主要职责是区分任务优先级,优先执行高优先级的任务;

新的架构在原来的基础上引入了Scheduler(调度器),这个东西是React团队参考浏览器的API:requestIdleCallback实现的。它的主要作用就是调度更新任务:

  • 一方面可以中断当前任务执行更高优先级的任务;
  • 另一方面能判断浏览器空闲时间,在恰当的时间将主动权给到浏览器,保证页面性能;并在浏览器下次空闲时继续之前中断的任务; 这样就将之前的不可中断的同步更新变成了异步可中断更新,不直接使用浏览器API可能考虑到兼容问题,可能也有别的方面的考量。

下面是新的React架构更新模型:

这个新的架构在进入Renderer之前的流程是可以被中断的,主要有下列两种情况:

  • 进入了更高优先级的任务;
  • 浏览器在当前帧没有剩余空闲时间了;

Fiber

Fiber简单的理解就是React15版本的虚拟DOM。

Fiber简单理解

如果将新的React架构比作一个公司,Fiber在新的架构里承担的就是这个公司的员工,员工也有等级,老板,部长,基层,每个人有自己的职责,知道自己在哪个节点该做什么工作,并将未完成的工作记住等第二天上班继续完成,从而保证公司的顺利运行。而每个Fiber对应一个React element

假如有这样一段代码:

function App() {
    return (
        <div>
            <span>牛牛</span>
            <span>不怕困难</span>
        </div>
     )
}

上面的代码的抽象Fiber树:

其中的每个方块都是一个Fiber,它们通过child, return, sibling连接对方构成一个Fiber树。相关参考视频讲解:传送门

Fiber结构

来看一个Fiber会有哪些属性:

function FiberNode(tag, pendingProps, key, mode) {
  // Instance
  this.tag = tag;   // 组件类型
  this.key = key;   // 组件props上的key
  this.elementType = null;      // ReactElement.type 组件的dom类型, 比如`div, p`
  this.type = null;     // 异步组件resolved之后返回的内容
  this.stateNode = null; // 在浏览器环境对应dom节点
  this.return = null;       // 指向父节点
  this.child = null;        // 孩子节点
  this.sibling = null;      // 兄弟节点, 兄弟节点的return指向同一个父节点
  this.index = 0;
  this.ref = null;          // ref
  this.pendingProps = pendingProps;     // 新的props
  this.memoizedProps = null;        // 上一次渲染完成的props
  this.updateQueue = null;          // 组件产生的update信息会放在这个队列
  this.memoizedState = null;        // // 上一次渲染完成的state
  this.dependencies = null;
  this.mode = mode; // Effects
  this.flags = NoFlags;     // 相当于之前的effectTag, 记录side effect类型
  this.nextEffect = null;   // 单链表结构, 便于快速查找下一个side effect
  this.firstEffect = null;  // fiber中第一个side effect
  this.lastEffect = null;   // fiber中最后一个side effect
  this.lanes = NoLanes;     // 优先级相关
  this.childLanes = NoLanes;  // 优先级相关
  this.alternate = null;    // 对应的是current fiber
}

Fiber工作原理

在弄明白Fiber工作原理之前,我们要先明确一个认知:新的React架构使用了两个Fiber树。

  • 一个Fiber树是当前页面dom的抽象,叫current
  • 另一个Fiber树是在内存中执行更新任务dom的抽象,叫workInProgress

这样做是为了方便比对变化组件,并降低创建的成本,尽可能复用现有代码逻辑,从而提高渲染效率。

mount

React代码在第一次执行时,因为页面还没有渲染出来,此时是没有current树的,只有一个正在构建DOM的workInProgress树。

假如我们有这样一段代码:

function App() {
    return (
        <div>
            <span>牛牛</span>
            <span>不怕困难</span>
        </div>
     )
}
ReactDOM.render(<App/>, document.querySelector('#root'));

基于上面的代码在mount会生成这样的Fiber树:

可以看到这个图只是在前面的图上增加了fiberRootrootFiber两个Fiber节点。

  • fiberRoot:整个React应用的根节点;
  • rootFiber: 某个组件树的根节点;(因为我们可能多次使用React.render()函数,这样就会有多个rootFiber)

图中此时fiberRoot对应的rootFiber下面还是空的,因为此时是第一次渲染,页面上没有任何东西,当workInProgress树构建完成,在mutation之后,layout之前,fiberRootd的current指针会指向workInProgress树,把它作为新的current树,此时结构会变成这样:

这时页面渲染完成了,等待下次触发更新时会从current树进行拷贝生成workInProgress树,然后比对更新。

update

如果我们在上面的代码中触发更新,将牛牛文本改成了勇敢牛牛,React代码就会开始进行任务调度,因为只有这一个任务,会马上执行,会从current树的rootFiber进行拷贝生成workInProgress树的根节点,在经过向下遍历比对,发现相同的就直接从current树上拷贝复用,直到比对到叶子节点的牛牛文本变了,这时才会生成新的Fiber(这里只是为了方便解释,其实我这里使用的代码牛牛不会生成新的Fiber,因为是纯文本,只会替换父级节点的props)

到此这篇关于React Fiber源码深入分析的文章就介绍到这了,更多相关React Fiber内容请搜索Devmax以前的文章或继续浏览下面的相关文章希望大家以后多多支持Devmax!

React Fiber源码深入分析的更多相关文章

  1. HTML实现代码雨源码及效果示例

    这篇文章主要介绍了HTML实现代码雨源码及效果示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  2. ios – React native链接到另一个应用程序

    如果是错误的,有人知道如何调用正确的吗?

  3. ios – React Native – 在异步操作后导航

    我正在使用ReactNative和Redux开发移动应用程序,我正面临着软件设计问题.我想调用RESTAPI进行登录,如果该操作成功,则导航到主视图.我正在使用redux和thunk所以我已经实现了异步操作,所以我的主要疑问是:我应该把逻辑导航到主视图?我可以直接从动作访问导航器对象并在那里执行导航吗?.我对组件中的逻辑没有信心.似乎不是一个好习惯.有没有其他方法可以做到这一点?

  4. 在ios中使用带有React Native(0.43.4)的cocoapods的正确方法是什么?

    我已经挖掘了很多帖子试图使用cocoapods为本地ios库设置一个反应原生项目,但我不可避免地在#import中找到了丢失文件的错误.我的AppDelegate.m文件中的语句.什么是使用反应原生的可可豆荚的正确方法?在这篇文章发表时,我目前的RN版本是0.43.4,而我正在使用Xcode8.2.1.这是我的过程,好奇我可能会出错:1)

  5. ios – React Native WebView滚动行为无法按预期工作

    如何确保滚动事件的行为与ReactNative应用程序中的浏览器相同?

  6. ios – React Native – BVLinearGradient – 找不到’React/RCTViewManager.h’文件

    谢谢.解决方法几天前我遇到了完全相同的问题.问题是在构建应用程序时React尚未链接.试试这个:转到Product=>Scheme=>管理方案…=>点击你的应用程序Scheme,然后点击Edit=>转到Build选项卡=>取消选中ParallelizeBuild然后点击标志添加目标=>搜索React,选择第一个名为React的目标,然后单击Add然后在目标列表中选择React并将其向上拖动到该列表中的第一个.然后转到Product=>再次清理并构建项目.这应该有所帮助.

  7. ios – React Native – NSNumber无法转换为NSString

    解决方法在你的fontWeight()函数中也许变成:

  8. ios – React native error – react-native-xcode.sh:line 45:react-native:command not found命令/ bin/sh失败,退出代码127

    尝试构建任何(新的或旧的)项目时出现此错误.我的节点是版本4.2.1,react-native是版本0.1.7.我看过其他有相同问题的人,所以我已经更新了本机的最新版本,但是我仍然无法通过xcode构建任何项目.解决方法要解决此问题,请使用以下步骤:>使用节点版本v4.2.1>cd进入[你的应用]/node_modules/react-native/packager>$sh./packager.s

  9. 反应原生 – 如何通过Xcode构建React Native iOS应用程序到设备?

    我试图将AwesomeProject应用程序构建到设备上.构建成功并启动屏幕显示,但后来我看到一个红色的“无法连接到开发服务器”屏幕.它表示“确保节点服务器正在运行–从Reactroot运行”npmstart“.看起来节点服务器已经运行,因为当我做npm启动时,我收到一个EADDRINUSE消息,表示该端口已经在使用.解决方法从设备访问开发服务器您可以使用开发服务器快速迭代设备.要做到这一点,你的

  10. 静音iOS推送通知与React Native应用程序在后台

    我有一个ReactNative应用程序,我试图获得一个发送到JavaScript处理程序的静默iOS推送通知.我看到的行为是AppDelegate中的didReceiveRemoteNotification函数被调用,但是我的JavaScript中的处理程序不会被调用,除非应用程序在前台,或者最近才被关闭.我很困惑的事情显然是应用程序正在被唤醒,并且它的didReceiveRemoteNotifi

随机推荐

  1. js中‘!.’是什么意思

  2. Vue如何指定不编译的文件夹和favicon.ico

    这篇文章主要介绍了Vue如何指定不编译的文件夹和favicon.ico,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  3. 基于JavaScript编写一个图片转PDF转换器

    本文为大家介绍了一个简单的 JavaScript 项目,可以将图片转换为 PDF 文件。你可以从本地选择任何一张图片,只需点击一下即可将其转换为 PDF 文件,感兴趣的可以动手尝试一下

  4. jquery点赞功能实现代码 点个赞吧!

    点赞功能很多地方都会出现,如何实现爱心点赞功能,这篇文章主要为大家详细介绍了jquery点赞功能实现代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  5. AngularJs上传前预览图片的实例代码

    使用AngularJs进行开发,在项目中,经常会遇到上传图片后,需在一旁预览图片内容,怎么实现这样的功能呢?今天小编给大家分享AugularJs上传前预览图片的实现代码,需要的朋友参考下吧

  6. JavaScript面向对象编程入门教程

    这篇文章主要介绍了JavaScript面向对象编程的相关概念,例如类、对象、属性、方法等面向对象的术语,并以实例讲解各种术语的使用,非常好的一篇面向对象入门教程,其它语言也可以参考哦

  7. jQuery中的通配符选择器使用总结

    通配符在控制input标签时相当好用,这里简单进行了jQuery中的通配符选择器使用总结,需要的朋友可以参考下

  8. javascript 动态调整图片尺寸实现代码

    在自己的网站上更新文章时一个比较常见的问题是:文章插图太宽,使整个网页都变形了。如果对每个插图都先进行缩放再插入的话,太麻烦了。

  9. jquery ajaxfileupload异步上传插件

    这篇文章主要为大家详细介绍了jquery ajaxfileupload异步上传插件,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  10. React学习之受控组件与数据共享实例分析

    这篇文章主要介绍了React学习之受控组件与数据共享,结合实例形式分析了React受控组件与组件间数据共享相关原理与使用技巧,需要的朋友可以参考下

返回
顶部