到底React Fiber架构是个什么

diff 算法缺陷

diff 算法问题出现在,React 的调度策略 -- Stack Reconfile。这个策略像函数调用栈一样,会深度优先遍历所有的 Virtual DOM 节点,进行Diff。它一定要等整棵 Virtual DOM 计算完成之后,才将任务出栈释放主线程。重点在于,Stack Reconfile始终会一次性地同步处理整个组件树。Stack Reconciler无法暂停,因此如果更新较为深入并且可用CPU时间有限,这种做法并非最优化的。所以,在浏览器主线程被 React更新状态任务占据的时候,用户与浏览器进行任何的交互都不能得到反馈,只有等到任务结束,才能突然得到浏览器的响应。

React Fiber 简介

使用协作式多任务处理任务。将原来的整个 Virtual DOM 的更新任务拆分成一个个小的任务。每次做完一个小任务之后,放弃一下自己的执行(可以暂停),将主线程空闲出来,看看有没有其他的任务。如果有的话,就暂停本次任务,执行其他的任务,如果没有的话,就继续下一个任务。

为什么React 需要 Fiber

这里特指Javascript 引擎是单线程运行的。严格来说,页面绘制由单独的GUI渲染进程负责,只不过GUI渲染线程和Javascript线程是互斥的. 另外底层的异步操作实际上也是多线程的。

  • JavaScript单线程。要是有一个任务长期霸占CPU,浏览器会呈现卡死的状

  • 因为不是所有的state更新都需要立即显示出来,比如屏幕之外的用户看不见的部分的更新

  • react 为了解决这个问题,提出了 Fiber reconciliation 的方案来代替之前的 Stack reconciliation。Fiber 相较于 Stack,采用了异步的方式将之前同步执行的计算过程拆分,这样主线程不会一直处于被占用的状态,可以有时间去处理其他任务,比如 I/O 操作,交互反馈等。

  • diff 算法满足不了暂停任务, React 通过Fiber 架构,让自己的 reconcilation 过程变成可被中断 适当让出CPU执行权

React Fiber思想

Fiber是堆栈的重新实现,专门用于React组件。你可以将单个Fiber视为一个虚拟堆栈帧。

时间切片:

  • 虚拟DOM,是可以进行分片进行

  • react 新API:unstable_deferredUpdates

  • Chorme 新API:requestIdleCallback

  • 时间切片让React 渲染的过程可以被中断。当遇到浏览器行为的时候,可以将控制权交回浏览器,让位给高优先级的任务(键盘鼠标输入行为),浏览器空闲后再介入进行渲染,适当让出CPU执行权还给浏览器

  • 当一个节点处理完成,react仍然会检查当前时间片是否够用。如果够用则处理下一个

  • fiber reconciler优先级策略:通过将reconciliation过程,分解成小的工作单元的方式,可以让页面对于浏览器事件的响应更加及时。但是另外一个问题还是没有解决,就是如果当前在处理的react渲染耗时较长,仍然会阻塞后面的react渲染。

  • Fiber 依次通过 return、child 及 sibling 的顺序对 ReactElement 做处理,将之前简单的树结构,变成了基于单链表的树结构,维护了更多的节点关系。

React Fiber主要阶段

  • render / reconciliation (interruptible) 协调/渲染 协调阶段通常被称为“渲染阶段”
    Reconcile 阶段。此阶段中,依序遍历组件,通过diff 算法,判断组件是否需要更新,给需要更新的组件加上tag。遍历完之后,将所有带有tag的组件加到一个数组中。这个阶段的任务可以被打断。

  • commit (not interruptible) 提交
    根据在 Reconcile 阶段生成的数组,遍历更新DOM,这个阶段需要一次性执行完。如果是在其他的渲染环境 -- Native,硬件,就会更新对应的元素。

requestIdleCallback API

window.requestIdleCallback()方法将在浏览器的空闲时段内调用的函数排队。这使开发人员能够在主事件循环上执行后台和低优先级工作,而不会影响延迟关键事件,如动画和输入响应。函数一般会按先进先调用的顺序执行,然而,如果回调函数指定了执行超时时间timeout,则有可能为了在超时前执行函数而打乱执行顺序。

兼容性:

react 使用 这个api ,根据当前主线程的使用情况去处理这次update,requestIdleCallback可以在这个空闲期(Idle Period)调用空闲期回调(Idle Callback),执行一些任务
目前 requestIdleCallback 目前只有Chrome, Opera支持。所以目前 React 自己实现了一个

页面流畅与 FPS:

  • 页面是一帧一帧绘制出来的,当每秒绘制的帧数(FPS)达到 60 时,页面是流畅的,小于这个值时,用户会感觉到卡顿。

  • 1s 60帧,所以每一帧分到的时间是 1000/60 ≈ 16 ms。所以我们书写代码时力求不让一帧的工作量超过 16ms。

一帧内需要完成如下六个步骤的任务:

  • 处理用户的交互
  • JS 解析执行
  • 帧开始。窗口尺寸变更,页面滚去等的处理
  • requestAnimationFrame(rAF)
  • 布局
  • 绘制

上面六个步骤完成后没超过 16 ms,说明时间有富余,此时就会执行 requestIdleCallback 里注册的任务。

  • 客户端线程执行任务时会以帧的形式划分,大部分设备控制在30-60帧是不会影响用户体验;在两个执行帧之间,主线程通常会有一小段空闲时间,requestIdleCallback可以在这个空闲期(Idle Period)调用空闲期回调(Idle Callback),执行一些任务 。对于不支持这个API的浏览器,react会加上pollyfill。
  • 低优先级任务由requestIdleCallback处理;
  • requestIdleCallback可以在多个空闲期调用空闲期回调,执行任务;
  • requestIdleCallback方法提供deadline,即任务执行限制时间,以切分任务,避免长时间执行,阻塞UI渲染而导致掉帧;

你可能感兴趣的:(到底React Fiber架构是个什么)