深入理解React中fiber

一、前言

Fiber是对React核心算法的重写,Fiber是React内部定义的一种数据结构,将更新渲染耗时长的大任务,分为许多的小片。Fiber节点保存啦组件需要更新的状态和副作用,一个Fiber代表一个工作单元。

二、Fiber在React做了什么

在react中,主要做了下面这些操作:

  • 为每个增加了优先级,优先级高的任务可以中断低优先级的任务。然后再重新,注意是重新执行优先级低的任务

  • 增加了异步任务,调用requestIdleCallback api,浏览器空闲的时候执行

  • dom diff树变成了链表,一个dom对应两个fiber(一个链表),对应两个队列,这都是为找到被中断的任务,重新执行

Fiber中的属性

type Fiber = {// 用于标记fiber的WorkTag类型,主要表示当前fiber代表的组件类型如FunctionComponent、ClassComponent等tag: WorkTag,// ReactElement里面的keykey: null | string,// ReactElement.type,调用`createElement`的第一个参数elementType: any,// The resolved function/class/ associated with this fiber.// 表示当前代表的节点类型type: any,// 表示当前FiberNode对应的element组件实例stateNode: any,// 指向他在Fiber节点树中的`parent`,用来在处理完这个节点之后向上返回return: Fiber | null,// 指向自己的第一个子节点child: Fiber | null,// 指向自己的兄弟结构,兄弟节点的return指向同一个父节点sibling: Fiber | null,index: number,ref: null | (((handle: mixed) => void) & { _stringRef: ?string }) | RefObject,// 当前处理过程中的组件props对象pendingProps: any,// 上一次渲染完成之后的propsmemoizedProps: any,// 该Fiber对应的组件产生的Update会存放在这个队列里面updateQueue: UpdateQueue<any> | null,// 上一次渲染的时候的statememoizedState: any,// 一个列表,存放这个Fiber依赖的contextfirstContextDependency: ContextDependency<mixed> | null,mode: TypeOfMode,// Effect// 用来记录Side EffecteffectTag: SideEffectTag,// 单链表用来快速查找下一个side effectnextEffect: Fiber | null,// 子树中第一个side effectfirstEffect: Fiber | null,// 子树中最后一个side effectlastEffect: Fiber | null,// 代表任务在未来的哪个时间点应该被完成,之后版本改名为 lanesexpirationTime: ExpirationTime,// 快速确定子树中是否有不在等待的变化childExpirationTime: ExpirationTime,// fiber的版本池,即记录fiber更新过程,便于恢复alternate: Fiber | null,
}

三、从架构的角度理解Fiber

增量渲染

把一个渲染任务分解成多个,然后分散在多个帧。实现任务可以中断、可以恢复,并且可以给不同的任务赋予不同的优先级,最终实现更加丝滑的用户体验。

React16之前,React的渲染和更新依赖分为Reconciler->Render,Reconciler对比新旧虚拟DOM(Document Object Model)的变化,Render将变化应用到视图。

React16加多了一个Scheduler,用来调度更新的优先级。更新的流程变成:每一个更新的任务都被赋予一个优先级,Scheduler把优先级高的先Reconciler,如果有一个优先级比之前的任务更高的,之前的任务会中断,执行完后,新一轮调度之前被中断的任务会重新Reconciler,继续渲染。

四、Fiber的concurrent模式

在React中,异步渲染中“时间切片”、“优先级”是Scheduler的核心能力,Scheduler在源码目录中与react-dom是同级的。

图片

我们都知道浏览器的刷新频率是60Hz,每16.6ms会刷新一次,没开启Concurrent模式,可以看到浏览器的Task中灰色的那长条不可中断任务,调用了createRoot后,那条大任务被切割成许个个小任务。切割后的小任务工作量加起来跟之前那条大任务是一样的,这就是“时间切片”效果。

如何实现时间切片

在源代码中,搜索workLoopSync函数就可以看到。

图片

   function wrokLoopSync () {while (workInProgress !== null) {performUnitOfWork(workInProgress)}}

同步渲染wrokLoopSync中while循环中触发下一个同步performUnitOfWork。

图片

异步渲染workLoopConcurrent中while循环触发也是performUnitOfWork,只不过多了一个shouldYield,这个是用来处理让出主进程的。

初略理解:

图片

React根据浏览器的帧率计算出时间切片大小,结合当前时间计算每一个切片的到期时间,workLoopConcurrent中每一个循环都会判断是否到期,让出主线程。

如何实现优先级调度

Scheduler中的unstable_scheduleCallback函数是一个核心方法,处理任务的优先级执行不同的调度逻辑。

在源码路径~/packages/scheduler/src/forks/Scheduler.js中可以看到这个方法。

function unstable_scheduleCallback(priorityLevel: PriorityLevel,callback: Callback,options?: {delay: number},
): Task {//  获取当前时间var currentTime = getCurrentTime();// 任务的预期开始时间var startTime;// 处理options的入参if (typeof options === 'object' && options !== null) {var delay = options.delay;// 如果定义了延迟时间,在加上这个延迟时间if (typeof delay === 'number' && delay > 0) {startTime = currentTime + delay;} else {startTime = currentTime;}} else {startTime = currentTime;}// 处理exoirationTime的计算依据var timeout;// 根据priorityLevel给timeout赋值switch (priorityLevel) {case ImmediatePriority:timeout = IMMEDIATE_PRIORITY_TIMEOUT;break;case UserBlockingPriority:timeout = USER_BLOCKING_PRIORITY_TIMEOUT;break;case IdlePriority:timeout = IDLE_PRIORITY_TIMEOUT;break;case LowPriority:timeout = LOW_PRIORITY_TIMEOUT;break;case NormalPriority:default:timeout = NORMAL_PRIORITY_TIMEOUT;break;}// 优先级越高,timeout越小,expirationTime越小var expirationTime = startTime + timeout;// 创建任务对象var newTask: Task = {id: taskIdCounter++,callback,priorityLevel,startTime,expirationTime,sortIndex: -1,};if (enableProfiling) {newTask.isQueued = false;}// 如果当前时间小于开始时间,说明该任务可以延迟(还没过期)if (startTime > currentTime) {// 延迟任务newTask.sortIndex = startTime;push(timerQueue, newTask);// 如果任务队列没有可以执行的任务,而且当前任务又是任务队列的第一个任务if (peek(taskQueue) === null && newTask === peek(timerQueue)) {// All tasks are delayed, and this is the task with the earliest delay.if (isHostTimeoutScheduled) {// Cancel an existing timeout.cancelHostTimeout();} else {isHostTimeoutScheduled = true;}// 派发一个延时任务,检查是否过期requestHostTimeout(handleTimeout, startTime - currentTime);}} else {// 处理任务过期逻辑newTask.sortIndex = expirationTime;// 过期任务推入taskQueuepush(taskQueue, newTask);if (enableProfiling) {markTaskStart(newTask, currentTime);newTask.isQueued = true;}// Schedule a host callback, if needed. If we're already performing work,// wait until the next time we yield.if (!isHostCallbackScheduled && !isPerformingWork) {isHostCallbackScheduled = true;// 执行taskQueue中的任务requestHostCallback();}}return newTask;
}

这个函数大概意思:

创建task,然后根据startTime任务的预期开始时间把task推入timerQueue或者taskQueue,最后根据timerQueue、taskQueue执行延时任务或者即时任务。

从上面的函数可以看出几个关键信息:

  • expirationTime越小,任务优先级越高

  • timerQueue是用来存储待执行的任务

  • taskQueue是用开存储过期的任务

五、Fiber中的一些函数

createFiber

mount过程中,创建了 rootFiber,是 react 应用的根 fiber。

function createFiber(tag: WorkTag,pendingProps: mixed,key: null | string,mode: TypeOfMode,
): Fiber {// $FlowFixMe[invalid-constructor]: the shapes are exact here but Flow doesn't like constructorsreturn new FiberNode(tag, pendingProps, key, mode);
}

Fiber的深度遍历

开始:Fiber 从最上面的 React 元素开始遍历,并为其创建一个 fiber 节点。

子节点:然后,它转到子元素,为这个元素创建一个 fiber 节点。这样继续下去直到在没有孩子

兄弟节点:现在,它检查是否有兄弟节点元素。如果有,它就遍历兄弟节点元素,然后再到兄弟姐妹的叶子元素。

返回:如果没有兄弟节点,那么它就返回到父节点。

createWorkInProgress

更新过程,创建 workInProgress fiber,对其标记副作用。

current Fiber 中每个 fiber 节点通过 alternate 字段,指向 workInProgress Fiber 中对应的 fiber 节点。同样 workInProgress Fiber 中的 fiber 节点的 alternate 字段也会指向 current Fiber 中对应的 fiber 节点。

源代码路径~/packages/react-reconciler/src/ReactFiber.js

图片

window.requestIdleCallback()

将在浏览器的空闲时段内调用的函数排队。方法提供 deadline,即任务执行限制时间,以切分任务,避免长时间执行,阻塞UI渲染而导致掉帧;

【安排低优先级或非必要的函数在帧结束时的空闲时间被调用】

requestAnimationFrame

图片

安排高优先级的函数在下一个动画帧之前被调用

六、最后

 React Fiber scheduler将工作分为多个工作单元。它设置每个工作的优先级,并使暂停、重用和中止工作单元。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/92501.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

快速排序与冒泡排序以及代码

快速排序 快速排序&#xff08;Quicksort&#xff09;是一种常用的排序算法&#xff0c;它基于分治的思想。 时间复杂度&#xff1a;O&#xff08;nlogn&#xff09; 空间复杂度&#xff1a;O&#xff08;logn&#xff09; 快速排序的基本思想如下&#xff1a; 选择一个元素…

xxl-job 2.2之后版本高版本executor未授权访问漏洞

xxl-job 低版本executor未授权访问 低版本的executor未授权访问漏洞是 POST /run HTTP/1.1 Host: your-ip:9999 Accept-Encoding: gzip, deflate Accept: */* Accept-Language: en User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like G…

如何初始化一个vue项目

如何初始化一个vue项目 安装 vue-cli 后 ,终端执行 vue ui npm install vue-cli --save-devCLI 服务 | Vue CLI (vuejs.org) 等一段时间后 。。。 进入项目仪表盘 设置其他模块 项目构建后目录 vue.config.js 文件相关配置 官方vue.config.js 参考文档 https://cli.vuejs.o…

BI神器Power Query(25)-- 使用PQ实现表格多列转换(1/3)

实例需求&#xff1a;原始表格包含多列属性数据,现在需要将不同属性分列展示在不同的行中&#xff0c;att1、att3、att5为一组&#xff0c;att2、att3、att6为另一组&#xff0c;数据如下所示。 更新表格数据 原始数据表&#xff1a; Col1Col2Att1Att2Att3Att4Att5Att6AAADD…

SEO搜索引擎

利用搜索引擎的规则提高网站在有关搜索引擎内的自然排名&#xff0c;吸引更多的用户访问网站&#xff0c;提高网站的访问量&#xff0c;提高网站的销售能力和宣传能力&#xff0c;从而提升网站的品牌效应 搜索引擎优化的技术手段 黑帽SEO 通过欺骗技术和滥用搜索算法来推销毫不…

黑豹程序员-架构师学习路线图-百科:Git/Gitee(版本控制)

文章目录 1、什么是版本控制2、特点3、发展历史4、SVN和Git比较5、Git6、GitHub7、Gitee&#xff08;国产&#xff09;8、Git的基础命令 1、什么是版本控制 版本控制系统&#xff08; Version Control &#xff09;版本控制是一种管理和跟踪软件开发过程中的代码变化的系统。它…

嵌入式Linux应用开发-基础知识-第十八章系统对中断的处理③

嵌入式Linux应用开发-基础知识-第十八章系统对中断的处理③ 第十八章 Linux系统对中断的处理 ③18.5 编写使用中断的按键驱动程序 ③18.5.1 编程思路18.5.1.1 设备树相关18.5.1.2 驱动代码相关 18.5.2 先编写驱动程序18.5.2.1 从设备树获得 GPIO18.5.2.2 从 GPIO获得中断号18.5…

【QT】使用toBase64方法将.txt文件的明文变为非明文(类似加密)

目录 0.环境 1.背景 2.详细代码 2.1 .h主要代码 2.2 .cpp主要代码&#xff0c;主要实现上述的四个方法 0.环境 windows 11 64位 Qt Creator 4.13.1 1.背景 项目需求&#xff1a;我们项目中有配置文件&#xff08;类似.txt&#xff0c;但不是这个格式&#xff0c;本文以…

C#,数值计算——Ranhash的计算方法与源程序

1 文本格式 using System; namespace Legalsoft.Truffer { /// <summary> /// High-quality random hash of an integer into several numeric types. /// </summary> public class Ranhash { public Ranhash() { }…

【新版】系统架构设计师 - 未来信息综合技术

个人总结&#xff0c;仅供参考&#xff0c;欢迎加好友一起讨论 文章目录 架构 - 未来信息综合技术考点摘要信息物理系统CPS的体系架构CPS 的技术体系CPS应用场景 人工智能分类关键技术机器学习 机器人发展分类机器人4.0 边缘计算概念与特点边云协同安全应用场景 数字孪生关键技…

前端开发和后端开发的一些建议

前端开发和后端开发是Web开发的两个方向 前端开发主要负责实现用户在浏览器上看到的界面和交互体验&#xff0c;包括HTML、CSS和JavaScript等技术。后端开发主要负责处理服务器端的逻辑和数据&#xff0c;包括数据库操作、服务器配置和接口开发等技术。 前端开发 前端开发需…

GitHub上有助于开发微信小程序的仓库

2023年9月30日&#xff0c;周六晚上 最近帮同学在GitHub找了一些开发小程序会用到的东西 目录 UI库WePY框架基于WePY框架的Demo微信小程序开发资源汇总 UI库 GitHub - Tencent/weui-wxss: A UI library by WeChat official design team, includes the most useful widgets/m…

2023年9月随笔之摩托车驾考

1. 回头看 日更坚持了273天。 读《SQL学习指南&#xff08;第3版&#xff09;》更新完成 读《高性能MySQL&#xff08;第4版&#xff09;》持续更新 学信息系统项目管理师第4版系列持续更新 9月码字81307字&#xff0c;日均码字数2710字&#xff0c;累计码字451704字&…

Explain执行计划字段解释说明---ID字段说明

ID字段说明 1、select查询的序列号,包含一组数字&#xff0c;表示查询中执行select子句或操作表的顺序 2、ID的三种情况 &#xff08;1&#xff09;id相同&#xff0c;执行顺序由上至下。 &#xff08;2&#xff09;id不同&#xff0c;如果是子查询&#xff0c;id的序号会…

基于Java的厨艺交流平台设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作…

【算法分析与设计】贪心算法(下)

目录 一、单源最短路径1.1 算法基本思想1.2 算法设计思想1.3 算法的正确性和计算复杂性1.4 归纳证明思路1.5 归纳步骤证明 二、最小生成树2.1 最小生成树性质2.1.1 生成树的性质2.1.2 生成树性质的应用 2.2 Prim算法2.2.1 正确性证明2.2.2 归纳基础2.2.3 归纳步骤2.3 Kruskal算…

debian设置允许ssh连接

解决新debian系统安装后不能通过ssh连接的问题。 默认情况下&#xff0c;Debian系统不开启SSH远程登录&#xff0c;需要手动安装SSH软件包并设置开机启动。 > 设置允许root登录传送门&#xff1a;debian设置允许root登录 首先检查/etc/ssh/sshd_config文件是否存在。 注意…

文献阅读:RLAIF: Scaling Reinforcement Learning from Human Feedback with AI Feedback

文献阅读&#xff1a;RLAIF: Scaling Reinforcement Learning from Human Feedback with AI Feedback 1. 文章简介2. 方法介绍 1. 整体方法说明 3. 实验结果 1. RLHF vs RLAIF2. Prompt的影响3. Self-Consistency4. Labeler Size的影响5. 标注数据的影响 4. 总结 & 思考 文…

CVE-2020-11978 Apache Airflow 命令注入漏洞分析与利用

简介 漏洞软件&#xff1a;Apache Airflow影响版本&#xff1a;< 1.10.10 环境 Vulhub 漏洞测试靶场 复现步骤 进入 /root/vulhub/airflow/CVE-2020-11978/ 目录运行以下命令启动环境 # 初始化数据库 docker compose run airflow-init # 开启服务 docker compose up -…

ChatGPT 现在可以看、听和说话了!

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…