Master - React Fiber

参考:

  1. https://juejin.cn/post/7451649674358046739
  2. https://juejin.cn/post/7322296552202272804
  3. https://www.cnblogs.com/beifeng1996/p/16821066.html
  4. https://blog.csdn.net/HUSHILIN001/article/details/135755289
  5. https://xie.infoq.cn/article/ad40e50d8456dfa96ef26dd54
  6. https://www.cnblogs.com/beifeng1996/p/16821070.html (VDOM、Diff、key
  7. https://www.cnblogs.com/beifeng1996/p/16821058.html (JSX、ReactElement
  8. https://www.cnblogs.com/beifeng1996/p/16804934.html (React通信方式
  9. https://www.infoq.cn/article/flex4gdzigdmjueq4orw

Fiber

Fiber是什么?

React Fiber是React16版本引入的,重写了React应用渲染相关的计算更新逻辑目的是 独立执行每个虚拟DOM的调度和协调,而不是执行整个VDOM树的调度和协调
1. 从架构层面:Fiber Reconcielr替换掉React16版本之前的Stack Reconciler Stack Reconciler:是React16版本之前通过堆栈结构来递归遍历组件树获取最新虚拟DOM。这个计算过程是同步不可中断 这样就会出现面对复杂计算量大的渲染任务时长时间占用主线程阻塞GUI渲染线程、UI交互线程、网络请求线程执行造成页面卡顿情况出现 而Fiber Reconciler能够实现异步可中断渲染(时间分片和任务优先级调度 ),将渲染任务分割成多个更小的任务单元,这个任务单元会在多个时间片内完成并在执行每个任务单元之前会根据任务的优先级进行调度或中断
2. 从数据结构层面:FiberNode是React应用最小工作单位是每个FiberNode都有一个ReactElement相对应,ReactElement是基于描述UI组件的jsx代码
UI组件渲染相关的属性:
基础属性(key、type、tag)
组件状态属性(pendingProps、memoizedProps、memoizedState以及stateNode
节点关系属性(child、sibling、return)

UI组件调度更新相关的属性:
副作用属性(flags:节点操作 placement:新节点插入、update现有节点更新、delete现有节点删除)、nextEffect:下一个副作用
任务优先级相关属性(updateQueue: 存储当前组件更新请求、lane:存储FiberNode对应的渲染任务的优先级
双缓冲机制属性(alternate: 存储新旧FiberNode节点对比信息)

FiberNode对应一个工作单元也是一个渲染任务会分割成多个小的任务在多个时间片中执行

Fiber有什么用?解决之前版本什么问题?

是React16版本之前通过堆栈结构来递归遍历组件树获取最新虚拟DOM。这个计算过程是同步不可中断
这样就会出现面对复杂计算量大的渲染任务时长时间占用主线程阻塞GUI渲染线程、UI交互线程、网络请求线程执行造成页面卡顿情况出现
而Fiber Reconciler能够实现异步可中断渲染(时间分片和任务优先级调度 ),将渲染任务分割成多个更小的任务单元,这个任务单元会在多个时间片内完成并在执行每个任务单元之前会根据任务的优先级进行调度或中断

什么是Fiber Tree? 和传统Virtual DOM有什么区别

Fiber Tree是由多个Fiber节点组成的树状结构,表示React组件及其子组件的渲染结构。在React应用的更新过程中,Fiber Tree负责协调和调度UI的更新。 React16引入的Fiber架构是基于VDOM的只是不再使用新旧虚拟DOM进行一次性的不可中断的diffing这个过程也就是一个周期内的渲染任务处理 Fiber架构对一个周期内渲染任务处理分割成更小的工作单元,这些工作单元可根据各自分配的优先级进行先后处理而且可以整个处理的过程可以分散到多个时间片内容完成也就是使得diffing的过程是异步可中断的不会阻塞应用中其他任务的执行。

FiberTree和FiberNode关系

Fiber Tree是由多个Fiber节点组成的树状结构,表示React组件及其子组件的渲染结构。在React应用的更新过程中,Fiber Tree负责协调和调度UI的更新。
FiberNode是FiberTree的子节点,每个FiberNode描述对应组件的状态和更新任务 eg: 渲染信息、更新任务、生命周期状态、任务优先级

React Fiber的工作单元(Work Unit)是什么?

工作单元(Work Unit):Fiber的更新过程是基于工作单元的,
每次更新可能会将一个或多个Fiber节点划分为小的工作单元,进行增量渲染。
这种方式让React能够分片更新UI,避免了长时间阻塞主线程,提高了应用的响应性。

React Fiber的执行机制(渲染机制的核心)

Fiber是怎么实现渲染可中断的?

https://juejin.cn/post/7092419515748712456
https://blog.csdn.net/weixin_57208584/article/details/132523633?fromshare=blogdetail&sharetype=blogdetail&sharerId=132523633&sharerefer=PC&sharesource=qq_41007584&sharefrom=from_link
https://www.zhihu.com/question/523295803

1. 🥩首先是渲染可中断中断的是什么?
React Fiber会将渲染任务拆分成更小的工作单元也就是FiberNode
FiberNode工作单元(1.新旧FiberNodediffing、2.计算标记副作用、3. 生命周期方法执行)执行过程会划分到多个时间片内执行 中断的就是这个执行过程。
2. 🥩 渲染任务中断的时机是什么?
第一个时机:时间片到期和任务已过期
第二个时机:低优先级任务被挂载后再次执行前会先检查任务队列中是否存在更高优先级任务,有则先执行
2. 🥩如何实现渲染任务可中断?
主要通过Fiber树结构、时间分片、任务优先级分配、事件循环机制(MessageChannel)实现的
1. Fiber树结构相关
React Fiber将渲染任务分割成更小的工作单元,每个单元可单独中断和继续执行
2. 时间分片(Time Slicing)
React将工作单元执行的过程划分到多个时间片内执行,通过时间片限制在帧与帧之间的空闲时间中执行的过程
当时间片到期时,渲染任务会被挂载,当前任务会暂停,直到下一个空闲时间片
3. 任务优先级分配
React Fiber划分成更小的工作单元调度器会分配每个工作单元的优先级别并存储到lane属性上,
工作单元进行任务队列后会在浏览器帧和帧之间的空闲时间根据优先级从任务队列中取出并执行
4. 事件循环机制
React Fiber中工作单元会被插入到事件循环的宏任务队列中
React调度器会先执行高优先级的任务,当一个低优先级任务执行时间片到期并挂载会在下一个周期执行前检查是否存在有高优先级任务,有则先执行
🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨源码层面 在 React 中,MessageChannel 和 shouldYield() 的结合用于实现 任务的中断,从而实现 时间分片(Time Slicing)。具体来说,MessageChannel 用于触发浏览器在空闲时间执行异步任务,而 shouldYield() 则用于决定是否需要让出当前任务的控制权,从而允许事件循环中的其他任务(如用户输入、UI 渲染)先执行。
🚨🚨🚨执行流程
1. 当前任务执行时:React 在执行任务(例如渲染更新)时,会不断检查 shouldYield()。 shouldYield() 会判断当前任务是否已经执行超过了一定时间(即时间片是否用完)。如果是,shouldYield() 会返回 true,表示应该让出控制权。
3. 通过 MessageChannel 调度任务:
如果 shouldYield() 返回 true,React 会 暂停当前任务,并通过 port.postMessage(null) 向 port1 发送消息,通知事件循环执行其他任务。 port1.onmessage 触发的回调(例如 performWorkUntilDeadline)会在 下一次事件循环中执行。 下一次事件循环:

当事件循环重新开始时,浏览器会执行 port1.onmessage 的回调函数(即执行 performWorkUntilDeadline),继续处理任务。
这样,任务就被拆分成了多个时间片,每个时间片执行一小部分任务,在任务之间允许浏览器处理高优先级的任务(如用户输入、渲染等)。

const channel = new MessageChannel();
const port = channel.port2;
// 任务执行回调
channel.port1.onmessage = performWorkUntilDeadline;
// 调度任务
schedulePerformWorkUntilDeadline = () => {
port.postMessage(null);
};
// 任务执行函数
function performWorkUntilDeadline() {
if (shouldYield()) {
// 如果应该让出控制权,继续调度任务
schedulePerformWorkUntilDeadline();
} else {
// 执行渲染任务
doWork();
}
}
// 判断是否应该让出控制权
function shouldYield() {
return currentTime > expirationTime; // 例如判断当前任务是否超时
}

Fiber是通过什么实现时间分片(Time Slicing)的?

React是通过Fiber Reconciler调度器实现时间分片的
通过将渲染任务划分为多个更小的工作单元(FiberNode),这些工作单元对应的执行过程会在多个时间片内执行,当执行时间小于时间片则执行其他任务否则挂起当前任务执行状态
源码层面即 shouldYieldToHost()中断 以及 MessageChannel调度
shouldYieldToHost() 中断任务:
shouldYieldToHost() 是 React 调度器中的一个重要函数,它用来判断当前任务是否应该暂停,是否应该让出控制权。
其主要依据是任务是否执行过长的时间(即任务的过期时间),如果当前任务执行超过了设定的时间片,shouldYieldToHost() 会返回 true,表示 React 需要 中断 当前任务并允许浏览器有机会执行其他任务(例如渲染、用户交互等)。
MessageChannel 调度任务:
MessageChannel 用于 React 在当前任务执行时通过事件循环 调度 下一个任务。
当 shouldYieldToHost() 判断任务应该暂停时,React 会使用 MessageChannel 通过 port.postMessage() 将任务调度到下一个事件循环周期中,从而避免长时间阻塞主线程。
port1.onmessage 上的回调函数(例如 performWorkUntilDeadline())会在下一次事件循环时被调用,继续执行任务。通过这种方式,React 可以将大的渲染任务切分成多个小的单元,并在多个事件循环中逐步完成,保持应用的响应性。

Fiber动态优先级是什么?

动态优先级指的是React在渲染过程中根据任务的紧急程度动态调整任务的优先级。确保高优先级任务尽快得到执行,而低优先级任务可以在浏览器空闲时间内处理

一个工作单元的优先级是通过任务的状态和调度确定的
lane: 任务的优先级别
expirationTime: 任务的过期时间。若一个任务的过期时间接近当前时间则任务的任务级会提高

Fiber中优先级的权重值

Immediate (Sync): 用于需要立即执行的任务。
User Blocking (High): 用于可能阻塞用户交互的任务,通常需要迅速响应。
Normal Priority (Normal): 一般渲染任务,普通的组件更新。
Low Priority (Low): 非关键的任务,通常可以在主线程空闲时执行。
Idle Priority (Idle): 用于浏览器完全空闲时执行的任务,通常不紧急。

Fiber是怎么做到让出控制权的?

https://juejin.cn/post/6953804914715803678#heading-4

本质上是JS如何让出控制权的,在一个事件循环中当产生宏任务时就会中断当前任务在本次事件循环中的执行并将控制权交给浏览器 Fiber中通过MessageChannel来产生宏任务
1. 将控制权交给浏览器,处理用户交互、网络请求的任务避免渲染卡顿
2. 浏览器更新页面后进行下一次事件循环会继续执行未完成的任务
React 中的 时间分片 机制使用了 shouldYield() 来判断是否该暂停当前任务,并将控制权交给浏览器。shouldYield() 结合 MessageChannel 共同决定是否应该在当前任务执行过程中让出控制权,以便浏览器能够及时处理更高优先级的任务(例如用户输入、UI 渲染等)。

🚨🚨🚨执行流程
当前任务执行时:
React 在执行任务(例如渲染更新)时,会不断检查 shouldYield()。
shouldYield() 会判断当前任务是否已经执行超过了一定时间(即时间片是否用完)。如果是,shouldYield() 会返回 true,表示应该让出控制权。
通过 MessageChannel 调度任务:

如果 shouldYield() 返回 true,React 会 暂停当前任务,并通过 port.postMessage(null) 向 port1 发送消息,通知事件循环执行其他任务。
port1.onmessage 触发的回调(例如 performWorkUntilDeadline)会在 下一次事件循环中执行。
下一次事件循环:

当事件循环重新开始时,浏览器会执行 port1.onmessage 的回调函数(即执行 performWorkUntilDeadline),继续处理任务。
这样,任务就被拆分成了多个时间片,每个时间片执行一小部分任务,在任务之间允许浏览器处理高优先级的任务(如用户输入、渲染等)。

Fiber为什么使用MessageChannel而不是requestIdleCallback?

1. MessageChannel相对于requestIdleCallback具有更好的兼容性 2. requestIdleCallback控制权在浏览器而MessageChannel可以让React应用更精确对渲染任务进行控制

Fiber中MessageChannel的作用?

总结就是shouldYield()中断任务执行,MessageChannel让出控制权并负责渲染任务的调度 shouldYield():用于判断当前任务是否已经执行超过了预定的时间片,如果是,则 中断 当前任务的执行。这是 React 判断是否需要暂停任务的依据,它使得任务执行具备了可中断性。

MessageChannel:一旦 shouldYield() 判断出任务应该中断,React 使用 MessageChannel 通过 port.postMessage() 将当前任务挂起, 让出控制权 给浏览器的事件循环,从而使浏览器有机会处理更高优先级的任务(例如用户输入、UI 渲染等)。同时,MessageChannel 负责将渲染任务的调度和执行安排到 下一次事件循环。
🚨🚨🚨🚨🚨🚨
shouldYield(): 负责当前任务中断
MessageChannel:负责当前任务调度(安排在下一次事件循环中执行
🚨🚨🚨🚨🚨🚨
MessageChannel之所以能让出控制权和事件循环以及宏任务相关
一个渲染周期内的一个事件循环当任务执行出现宏任务时会安排当前任务在下一次事件循环中执行让出浏览器的控制权

为什么不用setTimeout而用MessageChannel

源码中有一段注释说明了setTimeout执行存在4ms的延迟

Fiber是什么样的数据结构

FiberNode是通过单向链表管理
return:指向父 Fiber 节点的指针
child:指向第一个子 Fiber 节点的指针
sibling:指向下一个兄弟 Fiber 节点的指针

Fiber必须是链表吗?可以用数组吗?

使用链表结构的原因是为了 高效地插入、删除和更新节点
🚨 插入和删除的性能:
如果 Fiber 使用数组而不是链表,虽然读取和遍历数组的元素是快速的,但 动态的插入和删除 操作可能会比较低效。在数组中插入或删除元素可能会导致大量的元素移动,尤其是在 数组头部或中间 进行插入时。
🚨 层次化结构的表示:
在链表中,child 和 sibling 关系可以直接用指针(引用)表示。若使用数组来表示层次结构,你需要额外的逻辑来追踪父子关系和兄弟关系,可能会增加额外的复杂性。

posted @   Felix_Openmind  阅读(11)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具
*{cursor: url(https://files-cdn.cnblogs.com/files/morango/fish-cursor.ico),auto;}
点击右上角即可分享
微信分享提示