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

文章的系列标题为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源码分析 第三篇(异步状态)

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

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

    先附上两张流程图 1.scheduleRootUpdate  这个函数主要执行了两个操作  1个是创建更新createUpdate并放到更新队列enqueueUpdate, 1个是执行sheculeW ...

  2. React Fiber源码分析 第一篇

    先附上流程图一张 先由babel编译, 调用reactDOM.render,入参为element, container, callback, 打印出来可以看到element,container,cal ...

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

    先附上流程图~ 调用setState时, 会调用classComponentUpdater的enqueueSetState方法, 同时将新的state作为payload参数传进 enqueueSetS ...

  4. MyBatis 源码分析——介绍

    笔者第一次接触跟MyBatis框架是在2009年未的时候.不过那个时候的他并不叫MyBatis,而是叫IBatis.2010年的时候改为现在的名字--MyBatis.这几年过去了,对于笔者来讲有一点陌 ...

  5. Java技术开发专题系列之【Guava RateLimiter】针对于限流器的入门到精通(针对于源码分析介绍)

    Guava包中限流实现分析 RateLimiter 之前的文章中已经介绍了常用的限流算法,而google在Java领域中使用Guava包中的限流工具进行服务限流. 回顾使用案例 Google开源工具包 ...

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

    Libevent介绍 安装 样例 Libevent介绍 在include\event2\event.h中有关于Libevent的介绍,这里简单翻译介绍一下: Libevent是以事件为驱动的开发可扩展 ...

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

    Spring框架提供了构建Web应用程序的全功能MVC模块,叫Spring MVC,通过Spring Core+Spring MVC即可搭建一套稳定的Java Web项目.本文通过Spring MVC ...

  8. 集合源码分析[3]-ArrayList 源码分析

    历史文章: Collection 源码分析 AbstractList 源码分析 介绍 ArrayList是一个数组队列,相当于动态数组,与Java的数组对比,他的容量可以动态改变. 继承关系 Arra ...

  9. MapReduce源码分析之LocatedFileStatusFetcher

    LocatedFileStatusFetcher是MapReduce中一个针对给定输入路径数组,使用配置的线程数目来获取数据块位置的实用类.它的主要作用就是利用多线程技术,每个线程对应一个任务,每个任 ...

随机推荐

  1. php获取当前时间的毫秒数

    floor(microtime()*1000); 用microtime能输出当前的秒的后面8位小数 乘以1000取整数就行了

  2. JS区分对象类型

    Object.prototype.toString.call() 区分对象类型 在JavaScript中数据类型分为:1.基本类型,2.引用类型 基本类型:Undefined,Boolean,Stri ...

  3. node06

    1.数据库: server端:数据存在 client端:管理工具,node mysql内有两个单位: 库:类似文件夹,容纳表 表:存储数据 行:一条数据 列(字段,域):一个数据项 主键:数据的唯一标 ...

  4. [Codeforces Round #516][Codeforces 1063B/1064D. Labyrinth]

    题目链接:1063B - Labyrinth/1064D - Labyrinth 题目大意:给定一个\(n\times m\)的图,有若干个点不能走,上下走无限制,向左和向右走的次数分别被限制为\(x ...

  5. 编译的时候出现 usr/bin/ld: cannot find -lxxx 的错误

    比如错误为: usr/bin/ld: cannot find -labc 首先要明白的是 labc 代表的是 libabc.so 明白了这个之后,我们就知道了问题是:在编译的过程中,需要 libabc ...

  6. DCOS实践分享(5):Open DCOS深入分析

    2016/05/18(三) 09:30 OpenDC/OS研討會暨工作坊 指導單位:經濟部.科技部 主辦單位:工業技術研究院.Linker Networks 協辦單位:Microsoft.資通訊產業聯 ...

  7. Python代码缩进与测试模块

    一.Python代码缩进 Python 函数没有明显的  begin 和  end ,没有标明函数的开始和结束的花括号.唯一的分隔符是一个冒号 ( : ),接着代码本身是缩进的. 例如:缩进  bui ...

  8. [Swift]LeetCode1026. 节点与其祖先之间的最大差值 | Maximum Difference Between Node and Ancestor

    Given the root of a binary tree, find the maximum value V for which there exists different nodes A a ...

  9. ASP.NET Core 系列目录

    目录: ASP.NET Core 2.0 : 一. 概述 ASP.NET Core 2.0:二. 开发环境 ASP.NET Core 2.0 : 三. 项目结构 ASP.NET Core 2.0 : ...

  10. ORA-01034: ORACLE not available问题

    通过DBCA新建一个数据库后,执行以下命令报错:SQL> shutdown immediate;ERROR:ORA-01034: ORACLE not availableORA-27101: s ...