学习猜想-react&Fiber篇1

58 阅读4分钟

前言:这是我这段时间学习后对react的一个总体理解与猜想。如有错误欢迎指正。

首先,理解fiber要从浏览器的渲染说起。

浏览器渲染机制

  • 解析HTML->DOM树
  • 解析CSS->CSSOM树
  • 合并 DOM 树和 CSSOM 树,生成渲染树(Render Tree),获取元素的尺寸、位置及可见性等信息
  • 布局(Reflow):根据渲染树计算元素的准确位置和大小
  • 重绘:根据css信息生成具体的准确的最终的样式信息到页面

浏览器渲染进程有哪些线程

  • GUI:这个线程负责渲染浏览器页面,解析HTML、CSS,构建DOM树、CSSOM树、渲染树,以及绘制页面。当界面需要重绘或由于某种操作引发回流时,该线程就会执行。值得注意的是,GUI渲染线程和JS引擎线程是互斥的,当JS引擎执行时,GUI线程会被挂起, GUI更新会被保存在一个队列中,等待JS引擎空闲时立即执行。
  • JS引擎:解析执行JS脚本。
  • 定时器线程
  • 事件触发
  • 异步HTTP请求

通过结合这两部分内容来看,可以得到页面构建渲染GUI、JS两个线程工作的流程图如下:

graph TD
GUI线程 --> DOM树 --> 渲染树RenderTree --> 重排回流 --> 重绘-->页面
GUI线程 --> CSSOM树 --> 渲染树RenderTree

JS线程--> DOM发生改变--> 大小位置发生改变影响布局 --> 重排回流  

DOM发生改变--> 内容文本发生改变不影响布局 --> 重绘

由于GUI渲染线程和JS引擎线程是互斥的,当JS引擎执行时,GUI线程会被挂起。

所以当DOM节点发生变化的时候引入diff算法计算节点的具体变化。原diff算法是递归比较新旧两个虚拟dom树相同节点type、内容等属性发生的变化,根据变化做出反应。

什么是Fiber

React Fiber 是 React 核心算法的一次重要重构,旨在提高 React 在动画、布局和手势等领域的性能和适用性。它的主要目标是增量渲染,即能够将渲染任务拆分为多个小任务,并分散到多个帧上执行。其他关键特性包括:

  • 任务可中断、恢复、复用:当有新的更新时,可以暂停当前渲染任务、恢复之前的任务或复用已完成的任务。
  • 优先级调度:为不同类型的更新分配优先级,以确保更重要的更新(如用户输入)优先处理。
  • 支持并发模式:提供新的并发原语,提高交互体验

为什么是Fiber

首先提到两个现代浏览器提供的api:

这两个api都提供了由用户代理决定的,在空闲时间自动执行队列任务的能力。React Fiber 借鉴了这些 API 的调度思想,使其渲染任务能够分批执行,并在空闲时间进行更新。

当然:

  • React 并没有直接依赖 requestIdleCallback,因为 requestIdleCallback 的执行时间不稳定,在不同浏览器上表现差异较大,特别是在高负载的情况下可能会延迟较长时间。
  • React 主要使用 MessageChannel 进行任务调度,而 requestAnimationFrame 主要用于同步 DOM 更新,例如动画帧渲染。
  • 在现代浏览器环境下,React Fiber 主要依赖 Scheduler 库进行任务优先级调度,而不是简单地使用 requestIdleCallback

React Fiber 替换了传统递归的调用栈。在 React 旧版协调过程中,每个组件的渲染都是一个函数调用,这些函数调用形成了 JavaScript 引擎的调用栈(Call Stack) ,导致:

  • 任务无法中断,必须等当前组件树遍历完成后才能继续其他任务。
  • 递归调用栈的深度不可控,可能会导致浏览器长时间卡顿。

React Fiber 通过链表结构实现了自己的任务管理,避免了 JavaScript 递归调用栈的限制,这使得:

  • 任务可以拆分,并在多个帧中执行(时间切片)。
    • 每个 Fiber 节点代表一个组件的更新任务
    • React 采用“工作循环” (work loop)的方式,在浏览器空闲时逐步执行这些任务:
    • React 会从 workInProgress Fiber 树的根节点开始工作,每次执行部分任务,然后交出控制权给浏览器。
    • 这个过程会被 requestIdleCallbackMessageChannel 触发,以确保任务不会长时间占用主线程。
  • 任务可以暂停、恢复,而不是像递归那样一口气执行到底。

React Fiber 通过时间切片(Time Slicing)技术,将渲染任务拆分到多个帧中执行,从而减少主线程阻塞,提高页面响应速度。