React Fiber源码分析 (介绍)

写了分析源码的文章后, 总觉得缺少了什么, 在这里补一个整体的总结,输出个人的理解~

文章的系列标题为Fiber源码分析, 那么什么是Fiber,官方给出的解释是:

React Fiber是对核心算法的一次重新实现。

ummm, 这样说实在是有点泛,详细分析一下

先从开发者角度来看 

实际上这次更新对于我们来说影响并不大,只是几个生命周期改变了,新引入的两个生命周期函数 getDerivedStateFromPropsgetSnapshotBeforeUpdate 以及在未来 v17.0 版本中即将被移除的三个生命周期函数componentWillMount,componentWillReeiveProps,componentWillUpdate,目前版本并不会影响原生命周期的使用,但不能和新的生命周期一起使用,也会被标记为不安全,下图为目前React的流程图:

其他的几乎没有任何影响,我们还是照常的写着原来的代码,然后我们就感觉到网页性能更高了一些。

为什么网页性能会变高,Fiber做了什么?

要回答这个问题,需要回头看javascript是单线程的知识点。

单线程一次只能做一件事, 在原来的React中, 如果一次更新的时间比较长,那么用户就会感觉到卡顿,也就是丢帧了。

打个比方, 假如我现在要更新1000个组件(往大了说),每个组件平均花时间1ms,那么在1s内,浏览器的整个线程都被阻塞了,这时候用户在input上的任何操作都不会有反应,等到更新完毕,界面上突的一下就显示了原来用户的输入,这个体验是非常差的。这里借用官方一张图, Fiber之前的版本就是这样,调用栈非常深

那么Fiber,现在是怎么做呢?

Fiber实际上是把一次更新拆成一个个的单元任务,每次做完一个单元任务后,就询问是否有更高的优先级任务,有就去执行,回头再来干这件事,如图

那么就明白了,Fiber是一个任务调和器!, 同样,我们根据这个来分析Fiber具体做了什么

Fiber具体做了什么

首先,要做到这样的效果,那么就需要有以下的功能:

1.可分片  (拆分任务)

2.可中断   (执行另一个任务后, 可以回头继续执行未完成的任务)

3.具备优先级 (哪个任务先执行)

想要做到拆分任务就需要任务可以分片,也就是React的Fiber,fiber即为一个分片任务,贴上数据结构:

可中断即是使用了队列的形式保存任务, 具体可以看源码~

基本是一个fiber即为一个组件,而优先级即使用fiber的expirationTime属性, expirationTime越小即优先级越高

function FiberNode(tag, pendingProps, key, mode) {
  // Instance
  this.tag = tag;
  this.key = key;
  this.elementType = null;
  this.type = null;
  this.stateNode = null;

  // Fiber
  this.return = null;
  this.child = null;
  this.sibling = null;
  this.index = 0;

  this.ref = null;

  this.pendingProps = pendingProps;
  this.memoizedProps = null;
  this.updateQueue = null;
  this.memoizedState = null;
  this.firstContextDependency = null;

  this.mode = mode;

  // Effects
  this.effectTag = NoEffect;
  this.nextEffect = null;

  this.firstEffect = null;
  this.lastEffect = null;

  this.expirationTime = NoWork;
  this.childExpirationTime = NoWork;

  this.alternate = null;

} 

从数据结构上, 有几个属性值得说一下,

首先是官方注释了Fiber的几个属性, 这几个是非常重要的

原来的React更新任务是采用递归的形式, 那么现在如果任务想中断, 在递归中是很难做处理的, 所以React改成了大循环的模式

修改了生命周期也是因为任务可中断~

具体可以参考下面这篇文章

到目前为止(React 16.4),React的渲染机制遵循同步渲染:
1) 首次渲染: willMount > render > didMount,
2) props更新时: receiveProps > shouldUpdate > willUpdate > render > didUpdate
3) state更新时: shouldUpdate > willUpdate > render > didUpdate
3) 卸载时: willUnmount

期间每个周期函数各司其职,输入输出都是可预测,一路下来很顺畅。

BUT 从React 17 开始,渲染机制将会发生颠覆性改变,这个新方式就是 Async Render。

首先,async render不是那种服务端渲染,比如发异步请求到后台返回newState甚至新的html,这里的async render还是限制在React作为一个View框架的View层本身。

通过进一步观察可以发现,预废弃的三个生命周期函数都发生在虚拟dom的构建期间,也就是render之前。在将来的React 17中,在dom真正render之前,React中的调度机制可能会不定期的去查看有没有更高优先级的任务,如果有,就打断当前的周期执行函数(哪怕已经执行了一半),等高优先级任务完成,再回来重新执行之前被打断的周期函数。这种新机制对现存周期函数的影响就是它们的调用时机变的复杂而不可预测,这也就是为什么”UNSAFE”。
---------------------
作者:辰辰沉沉大辰沉
来源:CSDN
原文:https://blog.csdn.net/Napoleonxxx/article/details/81120854 

什么是大循环?

即执行某个fiber后, 会执行他的子元素, 如果没有子元素, 则兄弟元素, 然后又回到父元素, 父兄弟元素...

而寻找元素则是根据其上面几个属性return(父元素),child(子元素),sibiling(兄弟元素)

假设有以下的代码:

<div>
   <span1></span1>
   <p>
     <span2><span2>
   </p>
</div>

他的执行如图

Fiber的优先级?

再下来, fiber又是怎么做到根据优先级执行任务时不会卡顿呢,如果任务很多, 无穷无尽, 那不是一样会丢帧?

这时候就是requestIdleCallback这个API的骚操作了, 这个API是干嘛的呢?

window.requestIdleCallback()会在浏览器空闲时期依次调用函数, 这就可以让开发者在主事件循环中执行后台或低优先级的任务,而且不会对像动画和用户交互这样延迟触发而且关键的事件产生影响。函数一般会按先进先调用的顺序执行,除非函数在浏览器调用它之前就到了它的超时时间。

也就是说React实际上利用这个API在浏览器空闲期执行任务, 而这个API的回调有个参数deadline , 当你超时的时候,无论是不是在空闲期都会执行该任务, 这也就解释了为什么React采用时间来做优先级

不过实际上, React并没有在版本中使用了这个API,而是通过requestAnimationFrame来hack,强行设置每一帧的到期时间为requestAnimationFrame回调函数的参数加上33ms

var animationTick = function (rafTime) {
    isAnimationFrameScheduled = false;
    ...
    ...
    // 每帧到期时间为33ms
    frameDeadline = rafTime + 33
    if (!isIdleScheduled) {
      isIdleScheduled = true;
      window.postMessage(messageKey, ‘*‘);
    }
  };

当然了, 分优先级是有一个无法避免的问题, 那就是当有无数的优先级更高的任务插进来, 就会形成饥饿现象,原有的任务会一直得不到机会执行

后面还有一个打了注释Effect标签的几个属性,这几个属性主要是收集每次更新的结果, 并在最后一层层往上迭代, 最后由最高的节点收集, 并执行更新。

在分析的过程中,发现了React的源码中使用了很多链式结构, 回调链,任务链等, 这个主要是为了增删时性能比较高

最后总结一下:

React Fiber实际上就是一个任务调和器,它做到了将每一次更新切分成任务分片,从而拥有了可中断且有优先级的进行其他任务的功能。

如果想看源码, 可以参考本系列的另外三篇文章

React Fiber源码分析 第一篇

React Fiber源码分析 第二篇(同步模式)

React Fiber源码分析 第三篇(异步状态)

原文地址:https://www.cnblogs.com/Darlietoothpaste/p/9941117.html

时间: 2024-10-19 06:11:22

React Fiber源码分析 (介绍)的相关文章

React Fiber源码分析 第三篇(异步状态)

先附上流程图~ 调用setState时, 会调用classComponentUpdater的enqueueSetState方法, 同时将新的state作为payload参数传进 enqueueSetState会先调用requestCurrentTime获取一个currentTime, function requestCurrentTime() { // 维护两个时间 一个renderingTime 一个currentSechedulerTime // rederingTime 可以随时更新 cu

MyBatis 源码分析——介绍

笔者第一次接触跟MyBatis框架是在2009年未的时候.不过那个时候的他并不叫MyBatis,而是叫IBatis.2010年的时候改为现在的名字--MyBatis.这几年过去了,对于笔者来讲有一点陌生了.而且那个时候他也没有这么出名.hibernate占了大部分市场.虽然笔者早年的时候查看过他的源码,但是并没有很深入去理解他.主要的原因是因为当时我还在看hibernate的源码.太累了所以就没有去认真的理解.现在笔者想要重新在来看一篇关于他的源码并加强对他的理解.也是对自己过程的一种回归吧.

libevent源码分析-介绍、安装、使用

Libevent介绍 安装 样例 Libevent介绍 在include\event2\event.h中有关于Libevent的介绍,这里简单翻译介绍一下: Libevent是以事件为驱动的开发可扩展的网络服务端的库. 开放的API设置事件的回调函数,当事件来暂时调用这个回调函数.它还支持信号和定时器. 开发人员仅仅须要简单的add/remove来将事件加入到event loop中,通过event_dispatch驱动event. Libevent支持 /dev/poll, kqueue(2),

FastText总结,fastText 源码分析

文本分类单层网络就够了.非线性的问题用多层的. fasttext有一个有监督的模式,但是模型等同于cbow,只是target变成了label而不是word. fastText有两个可说的地方:1 在word2vec的基础上, 把Ngrams也当做词训练word2vec模型, 最终每个词的vector将由这个词的Ngrams得出. 这个改进能提升模型对morphology的效果, 即"字面上"相似的词语distance也会小一些. 有人在question-words数据集上跑过fastT

SpringMVC加载WebApplicationContext源码分析

from http://blessht.iteye.com/blog/2121845 Spring框架提供了构建Web应用程序的全功能MVC模块,叫Spring MVC,通过Spring Core+Spring MVC即可搭建一套稳定的Java Web项目.本文通过Spring MVC源码分析介绍它的核心实现原理. Tomcat服务器启动入口文件是web.xml,通过在其中配置相关的Listener和Servlet即可加载Spring MVC所需数据.基于Spring MVC最简单的配置如下.

深入理解 spring 容器,源码分析加载过程

Spring框架提供了构建Web应用程序的全功能MVC模块,叫Spring MVC,通过Spring Core+Spring MVC即可搭建一套稳定的Java Web项目.本文通过Spring MVC源码分析介绍它的核心实现原理. Tomcat服务器启动入口文件是web.xml,通过在其中配置相关的Listener和Servlet即可加载Spring MVC所需数据.基于Spring MVC最简单的配置如下. <!-- 加载Spring配置文件 --> <context-param>

MapReduce源码分析之LocatedFileStatusFetcher

LocatedFileStatusFetcher是MapReduce中一个针对给定输入路径数组,使用配置的线程数目来获取数据块位置的实用类.它的主要作用就是利用多线程技术,每个线程对应一个任务,每个任务针对给定输入路径数组Path[],解析出文件状态列表队列BlockingQueue<List<FileStatus>>.其中,输入数据输入路径只不过是一个Path,而输出数据则是文件状态列表队列BlockingQueue<List<FileStatus>>,文

OBS源码分析流程梳理

OBS-Studio是一款非常好用的开源直播推流软件,目前已经发布了很多个版本.但是目前都没看到详细或流程清晰的源码分析介绍.所以,本文以线程为单位对OBS的采集.编码.传输流程进行梳理,一方面能够使我自己更清楚OBS架构和运行流程,另一方面也能方便新手同学.整体的粒度不会太细,但已经能够阐述OBS流程.由于水平有限,其中可能存在问题,如果有,请指出. OBS主要线程共五个: 1.主线程:主要做一些初始化工作和UI处理 2.视频渲染线程:渲染视频到窗口 3.视频编码线程:编码原始视频 4.音频编

【React Native】源码分析之Native UI的封装和管理

??ReactNative作为使用React开发Native应用的新框架,随着时间的增加,无论是社区还是个人对她的兴趣与日递增.此文目的是希望和大家一起欣赏一下ReactNative的部分源码.阅读源码好处多多,让攻城狮更溜的开发ReactNative应用的同时,也能梳理RN项目的设计思路,增加自己的内功修为,^_^. ??好的,就让我们轻松的开始吧.此篇是以Android平台源码分析为主,分享Native UI的封装和管理,重点涉及react-native源码中com.facebook.rea