React Fiber
核心问题
- 递归无法中断,执行重任务耗时长,JS是单线程,无法同时执行其他任务, 导致任务延迟页面卡顿,用户体验差
what is react fiber? why react fiber?
React 15的问题: 如果页面元素很多,且需要频繁刷新的场景下.React 15会出现掉帧的现象.产生原因: 大量的同步计算任务阻塞了浏览器的UI渲染. 默认情况下,JS运算,页面布局和页面绘制都是运行在浏览器的主线程当中, 他们之间是互斥的关系. 如果JS计算持续占用主线程,页面就没办法得到及时的更新. 当我们调用setState更新页面的时候. React会遍历应用的所有节点,计算出差异, 然后再更新UI. 整个过程是一气呵成,不能被打断的. 如果页面元素很多,整个过程占用的时机就可能超过16毫秒, 就容易出现掉帧的情况.如何解决掉帧的问题: 解决主线程时间被JS运算占用着这一问题的基本思路,将运算切割未多个步骤,分批完成. 也就是说在完成一部分任务之后,将控制权交回给浏览器, 让浏览器有时间进行页面的渲染. 等浏览器忙会之后,再继续之前未完成的任务. ====>旧版React通过递归+循环的方式进行渲染,使用的是JS引擎自身的函数调用栈,它会一直执行到栈为空为止. 而Fiber实现了自己的组件调用栈, 它以链表的形式遍历组件树,可以灵活的暂停,继续和丢弃执行的任务。实现方式是使用了浏览器的requestIdleCallback这个API, 官方的解释如下
window.requestIdleCallback()会在浏览器空闲时期依次调用函数,这就可以让开发者在主事件循环中执行后台或低优先级的任务,而且不会对像动画和用户交互这些延迟触发但关键的事件产生影响。函数一般会按先进先调用的顺序来执行。除非函数在浏览器调用它之前就到了它的超时时间。
工作原理
- React框架内部的运作分为3层
Virtual DOM层,描述页面长什么样.Reconciler层,负责调用组件生命周期方法, 运行Diff运算等Renderer层,根据不同的平台,渲染相应的页面.比较常见的是ReactDOM和ReactNativeReconciler层在V16.xx版本中进行了非常大的改动,取了个新名字Fiber Reconciler
解决方案
-
- 利用浏览器空闲时间执行任务,拒绝长事件占用主线程
-
- 放弃递归只采用循环, 因为循环可被打断
-
- 任务拆分,将任务拆分成一个个小任务
实现思路:
在Fiber方案重,为了实现任务的终止再继续,DOM比对算法被分成了两部分
-
构建Fiber: 可中断
-
提交Commit: 不可中断
- DOM 初始渲染: VirtualDOM -> Fiber -> Fiber[] -> DOM
- DOM 更新操作: newFiber vs oldFiber -> Fiber[] -> DOM
Fiber
Fiber其实就是一种数据结构,用一个纯JS对象来表示
const fiber = {
type, // 节点类型
props, // 节点属性
stateNode, // 节点DOM对象 | 组件实例对象
tag, // 节点标记(对具体类型的分类 hostRoot || hostComponent || classComponent || functionComponent)
effects, // 数组, 存储需要更改的 fiber 对象
effectTag, // 当前Fiber 要执行的操作 (新增,删除,修改)
parent, // 当前Fiber的父级Fiber
child, // 当前Fiber的子级Fiber
sibling, // 当前Fiber的下一个兄弟Fiber
alternate, // Fiber备份 fiber比对时使用.
}
- 为了加以区分,以前的
Reconciler====>Stack Reconciler,过程不能打断.一条道走到黑. - 而
Fiber Reconciler每执行一段时间,分段执行
Fiber Scheduler(调度器)
- 为了达到
Fiber Reconciler的效果,需要一个调度器来进行任务分配和执行。任务的优先级有六种: -
synchronous,与在清华你的Stack Reconciler操作一样,同步执行
-
task,在next tick之前执行
-
animation,下一帧之前执行
-
high,在不久的将来立即执行
-
low,稍微延迟执行也没关系
-
offscreen,下一次render时或者scroll时才执行
- 优先级高的任务(键盘输入)可以打断优先级低的任务(Diff)的执行,从而更快的生效.
Fiber Reconciler 执行过程
阶段1, 生成Fiber树.得出需要更新的节点信息,这一步是一个渐进的过程,可以被打断.阶段2, 将需要更新的节点一次批量更新,这个过程补能被打断.- 阶段1可被打断的特性,让优先级更高的任务先执行,从框架层面大大降低了页面掉帧的概率.
Fiber Tree
Fiber Reconciler在阶段一进行Diff计算的时候, 会生成一棵Fiber Tree, 这棵树是在Virtual DOM树的基础上增加额外的信息来生成的.它的本质是一个链表.
requestIdleCallback & 16s
- 页面是一帧一帧绘制的,当每秒绘制的帧数达到60时, 页面就是流畅的, 小于这个值时, 用户会感觉到卡顿.
1 秒 60 帧, 每一帧就是1000/60 = 16ms,如果每一帧执行的时间小于16ms,说明浏览器有空余时间- 如果任务在剩余的时间内没有完成则会停止任务执行,继续优先执行主任务,也就是说
requestIdleCallback总是利用浏览器的空余时间执行任务.
requestIdleCallback(function(deadline){
// deadline.timeRemaining()
})
Keep learning

浙公网安备 33010602011771号