React Fiber源码分析 第一篇
React Fiber源码分析 第二篇(同步模式)
React Fiber源码分析 第三篇(异步状态)
React Fiber源码分析 第四篇(概括总结)javascript
React Fiber是React在V16版本中的大更新,利用了闲余时间看了一些源码,作个小记录~java
实际上此次更新对于咱们来讲影响并不大,只是几个生命周期改变了(React在版本中的更新简直作到了像一门语言同样,完美的兼容老版本,底层算法的大重构对于开发者来讲彻底透明),新引入的两个生命周期函数 getDerivedStateFromProps,getSnapshotBeforeUpdate 以及在将来 v17.0 版本中即将被移除的三个生命周期函数componentWillMount,componentWillReeiveProps,componentWillUpdate,目前版本并不会影响原生命周期的使用,但不能和新的生命周期一块儿使用,也会被标记为不安全,下图为目前React的流程图算法
其余的几乎没有任何影响,咱们仍是照常的写着原来的代码,而后咱们就感受到网页性能更高了一些。浏览器
要回答这个问题,须要回头看javascript是单线程的知识点。安全
单线程一次只能作一件事, 在原来的React中, 若是一次更新的时间比较长,那么用户就会感受到卡顿,也就是丢帧了。bash
打个比方, 假如我如今要更新1000个组件(往大了说),每一个组件平均花时间1ms,那么在1s内,浏览器的整个线程都被阻塞了,这时候用户在input上的任何操做都不会有反应,等到更新完毕,界面上突的一下就显示了原来用户的输入,这个体验是很是差的。这里借用官方一张图, Fiber以前的版本就是这样,调用栈很是深
网络
那么Fiber,如今是怎么作呢?数据结构
Fiber其实是把一次更新拆成一个个的单元任务,每次作完一个单元任务后,就询问是否有更高的优先级任务,有就去执行,回头再来干这件事,如图
异步
那么就明白了,Fiber是一个任务调和器!, 一样,咱们根据这个来分析Fiber具体作了什么函数
首先,要作到这样的效果,那么就须要有如下的功能:
在React中,不管是state仍是props的更新, 最后都操做在JSX的标签上
利用这种自然友好的表达,直接把每个标签当成一个任务分片如:div、p一、p二、span都是一个任务分片
<div>
<p>p1</p>
<p>
<span>p2</span>
</p>
</div>
复制代码
固然, 还要从标签转换成VDOM,再转成Fiber,才是一个真正的任务片,如图:
Fiber以前React是经过栈调度器进行递归更新,毕竟标签化是自然嵌套的,对递归友好,可是递归很差break和continue
Fiber则是以链表的形式来进行逐步更新(深度优先遍历算法),链表对break和continue友好Fiber节点拥有return, child, sibling三个属性,分别对应父节点, 第一个孩子, 它右边的兄弟,
React内部维护一个任务链表,每次某个任务结束后都会删除已完成的任务并继续执行其余可执行的任务,每一个任务都有一个finishedWork属性,若是该属性不为null,则说明更新完毕,只差commit render阶段
这个主要依赖于fiber中的两个属性expirationTime和childExpirationTime,当某个fiber被执行完毕后,会把expirationTime设为NoWork,即被打断后能够经过该属性判断任务碎片是否 须要执行
this.expirationTime = NoWork // 任务优先级
this.childExpirationTime = NoWork // 子任务片的优先级
复制代码
每次更新都不会对fiber直接操做,而是克隆一个做为alternater属性
更新队列, 存放更新的信息
收集更新信息,生成真实DOM
每一个Root任务\更新任务\fiber都具备expirationTime属性,该属性即为优先级expirationTime越小,优先级越高,同步模式下该值为0, 每一个层级的任务都是以链表的形式存在
这时候就是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, '*');
}
};
复制代码
固然了, 分优先级是有一个没法避免的问题, 那就是当有无数的优先级更高的任务插进来, 就会造成饥饿现象,原有的任务会一直得不到机会执行
React Fiber实际上就是一个任务调和器,它作到了将每一次更新切分红任务分片,从而拥有了可中断且有优先级的进行其余任务的功能。 在分析的过程当中,发现了React的源码中使用了不少链式结构, 回调链,任务链等,这个主要是为了增删时性能比较高