高级前端
react
【Q164】React 中 fiber 是用来做什么的

React 中 fiber 是用来做什么的

Issue 欢迎在 Gtihub Issue 中回答此问题: Issue 165 (opens in a new tab)

Author 回答者: yuzeyang97 (opens in a new tab)

因为JavaScript单线程的特点,每个同步任务不能耗时太长,不然就会让程序不会对其他输入作出相应,React的更新过程就是犯了这个禁忌,而React Fiber就是要改变现状。 而可以通过分片来破解JavaScript中同步操作时间过长的问题。

把一个耗时长的任务分成很多小片,每一个小片的运行时间很短,虽然总时间依然很长,但是在每个小片执行完之后,都给其他任务一个执行的机会,这样唯一的线程就不会被独占,其他任务依然有运行的机会。

React Fiber把更新过程碎片化,每执行完一段更新过程,就把控制权交还给React负责任务协调的模块,看看有没有其他紧急任务要做,如果没有就继续去更新,如果有紧急任务,那就去做紧急任务。

维护每一个分片的数据结构,就是Fiber。

Author 回答者: Feahter (opens in a new tab)

React Fiber是对核心算法的一次重新实现 Fiber reconciler 从v16.x开始底层使用Fiber reconciler替换stack reconciler. 已知: stack reconciler处理大状态时由于计算和组件树遍历的消耗容易出现渲染线程挂起,进而页面掉帧。(根本原因是渲染/更新过程一旦开始无法中断,持续占用主线程,主线程忙于执行JS)

求: 建立一种能解决主线程占用问题,且具有长远意义的机制 解: 把渲染/更新过程拆分为小块任务,通过合理的调度机制来控制时间(更细粒度、更强的控制力)

子问题:1.拆什么?什么不能拆? 把渲染/更新过程分为2个阶段(diff + patch): diffrender/reconciliation (对比prevInstance和nextInstance的状态,找出差异及其对应的DOM change。) patchcommit (把本次更新中的所有DOM change应用到DOM树,是一连串的DOM操作。) render/reconciliation阶段的工作(diff)可以拆分,commit阶段的工作(patch)不可拆分.

2.怎么拆? Fiber的拆分单位是fiber(fiber tree上的一个节点),实际上就是按虚拟DOM节点拆,因为fiber tree是根据vDOM tree构造出来的,树结构一模一样,只是节点携带的信息有差异。

3.如何调度任务? 分2部分: 工作循环 优先级机制 工作循环是基本的任务调度机制,工作循环中每次处理一个任务(工作单元),处理完毕有一次喘息的机会,此时通过shouldYield函数(idleDeadline.timeRemaining())判读时间是否用完,用完则把时间还给主线程等待下次requestIdleCallback的唤起,否则继续执行任务。 优先级机制用来处理突发事件与优化次序。 有如下策略: 到commit阶段了,提高优先级 高优任务做一半出错了,给降一下优先级 抽空关注一下低优任务,别给饿死了 如果对应DOM节点此刻不可见,给降到最低优先级 是工作循环的辅助机制。

4.如何中断/断点恢复? 中断:检查当前正在处理的工作单元,保存当前成果(firstEffect, lastEffect),修改tag标记一下,迅速收尾并再开一个requestIdleCallback,下次有机会再做 断点恢复:下次再处理到该工作单元时,看tag是被打断的任务,接着做未完成的部分或者重做 自然中断(时间耗尽),或优先级中断(高优任务中断),原理相同。

5.如何收集任务结果? 每个节点更新结束时向上归并effect list来收集任务结果,reconciliation结束后,根节点的effect list里记录了包括DOM change在内的所有side effect。

requestIdleCallback 让开发者在主事件循环中执行后台或低优先级的任务,不会对动画和用户交互等关键事件产生影响。

fiber 架构:

  • 循环条件:利用 requestIdeCallback 空闲时间递减.
  • 遍历过程:利用链表,找孩子找兄弟找父亲.