【React源码解析】深入理解react时间切片和fiber架构

时间切片

假如React一个更新需要耗时200ms,我们可以将其拆分为405ms的更新(后续会讲到如何拆分),然后每一帧里只花5ms来执行更新。那么,每一帧里不就剩余16.7 - 5 = 11.7ms的时间可以进行用户事件渲染等其他的js操作吗?如下所示:
image.png
那么这里就有两个问题:

  • 问题1:如何控制每一帧只执行5ms的更新?
  • 问题2:如何控制40个更新分配到每一帧里?

对于问题1比较容易,我们可以在更新开始时记录startTime,然后每执行一小段时间判断是否超过5ms。如果超过了5ms就不再执行,等下一帧再继续执行。

对于问题2,我们可以通过宏任务实现。比如5ms的更新结束了,那么我们可以为下一个5ms更新开启一个宏任务。浏览器则会将这个宏任务分配到当前帧或者是下一帧执行。

注意:
浏览器这一行为是内置的,比如设置 10000 个 setTimeout(fn, 0),并不会阻塞线程,而是浏览器会将这 10000 个回调合理分配到每一帧当中去执行。
比如:10000个个 setTimeout(fn, 0)在执行时,第一帧里可能执行了300个 setTimeout 回调,第二帧里可能执行了400个 setTimeout 回调,第 n 帧里可能执行了 200 个回调。浏览器为了尽量保证不掉帧,会合理将这些宏任务分配到帧当中去。

解决了上面两个问题,那么这个时候我们就有下面这种思路了:

  1. 更新开始,记录开始时间 startTime
  2. js 代码执行时,记录距离开始时间startTime是否超过了 5ms
  3. 如果超过了 5ms,那么这个时候就不应该再以同步的形式来执行代码了,否则依然会阻塞后续的代码执行。
  4. 所以这个时候我们需要把后续的更新改为一个宏任务,这样浏览器就会分配给他执行的时机。如果有用户事件进来,那么会执行用户事件,等用户事件执行完成后,再继续执行宏任务中的更新。

image.png
如上图所示,由于更新拆分成了一个个小的宏任务,从而使得click事件的回调有机会执行。

现在我们已经解决了更新阻塞的问题,接下来就需要解决如何将一个完整的更新拆分为多个更新,并且让它可以暂停等到click事件完成后再回来更新。

Fiber 架构

React传统的Reconciler是通过类似于虚拟DOM的方式来进行对比和标记更新。而虚拟DOM的结构不能很好满足将更新拆分的需求。因为它一旦暂停对比过程,下次更新时,很难找到上一个节点和下一个节点的信息,虽然有办法能找到,但是相对而言比较麻烦。所以,React团队引入了Fiber来解决这一问题。

每一个DOM节点对应一个Fiber对象,DOM树对应的Fiber结构如下:
image.png
(图片来自于这里)
Fiber通过链表的形式来记录节点之间的关系,它与传统的虚拟DOM最大的区别是多加了几个属性:

  • return表示父节点fiber
  • child表示子节点的第一个fiber
  • sibling表示下一个兄弟节点的fiber

通过这种链表的形式,可以很轻松的找到每一个节点的下一个节点或上一个节点。那么这个特性有什么作用呢?

结合上面提到的时间切片的思路,我们需要判断更新是否超过了5ms,我们以上面这棵Fiber树梳理一下更新的思路。从App Fiber开始:

  • 浏览器第一帧:
    • 记录更新开始时间startTime
    • 首先计算App节点,计算完成时,发现更新未超过5ms,继续更新下一个节点。
    • 计算div节点,计算完成时,发现更新超过了5ms,那么不会进行更新,而是开启一个宏任务。
  • 浏览器第二帧:
    • 上一帧最后更新的是div节点,找到下一个节点i am,计算该节点,发现更新未超过5ms,继续更新下一个节点。
    • 计算span节点,发现更新超过了5ms,那么不会进行更新,而是开启一个宏任务。
  • 浏览器第三帧:
    • 上一帧最后更新的是span节点,找到下一个节点KaSong,计算该节点,更新完成。

image.png

注:

  1. 实际的更新过程是 beginWork / completeWork 递与归的阶段,与这里有出入,这里仅做演示介绍。
  2. 这里的更新过程有可能不是第二帧和第三帧,而是在一帧里执行完成,具体需要看浏览器如何去分配宏任务。
  3. 更新过程分为 reconciler 和 commit 阶段,这里只会将 reconciler 阶段拆分。而 commit 阶段是映射为真实 DOM,无法拆分。

对应浏览器中的执行过程如下:
image.png
在这个过程中,每个节点计算完成后都会去校验更新时间是否超过了5ms,然后找到下一个节点继续计算,而双向链表恰恰是切合这种需求。

小结

通过上面的分析,我们可以总结成以下思路:

  1. 更新时遍历更新每一个节点,每更新一个Fiber节点后,会判断累计更新时间是否超过5ms
  2. 如果超过5ms,将下一个更新创建为一个宏任务,浏览器自动为其分配执行时机,从而不阻塞用户事件等操作。
  3. 如果更新的过程中,用户进行触发了点击事件,那么会在5ms与下一个5ms的间隙中去执行click事件回调。

通过以上步骤,我们能够将现有的同步更新转变为多个小更新分配到浏览器帧里,并且不会阻塞用户事件。接下来看看在React中实际是如何做到的。

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

立足本土,面向全球 | 全视通闪耀亮相Medical Fair Asia新加坡医疗展

Medical Fair Asia是亚洲地区最大的医疗设备、医疗器械和医疗技术展览会之一,自1997年创办以来,每两年在新加坡举办一次。该展会不仅是新加坡医疗行业交流的龙头平台,也是亚洲乃至全球医疗企业和专业人士共聚一堂、展示最新产品和技术的重要舞…

非关系型数据库Redis

文章目录 一,关系型数据库和非关系型数据可区别1.关系型数据库2.非关系型数据库3.区别3.1存储方式3.2扩展方式3.2事务性的支持 二,非关系型数据为什么产生三,Redis1.Redis是什么2.Redis优点3.Redis适用范围4. Redis 快的原因4.1 基于内存运行…

某讯/企鹅滑块验证码逆向(一)

文章目录 免责声明前言请求分析collect参数 总结 免责声明 本文章中所有内容仅供学习交流使用,不用于其他任何目的,不提供完整代码,抓包内容、敏感网址、数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由…

面试爱考 | 设计模式

一、概述二、创建型 1. 单例(Singleton) IntentClass DiagramImplementationExamplesJDK 2. 简单工厂(Simple Factory) IntentClass DiagramImplementation 3. 工厂方法(Factory Method) IntentClass Diagr…

C/C++教程学习视频网盘资源分享

正如大家所了解的,C语言和C是两种编程语言,它们有一些相似之处,也有一些明显的区别。今天与大家一起来探讨、学习和回顾一下,希望对正在学习或准备学习编程语言的小伙伴,能有帮助哦! C/C区别之处&#xff…

Linux环境使用Git同步教程

📖 前言:由于CentOS 7已于2024年06月30日停止维护,为了避免操作系统停止维护带来的影响,我们将把系统更换为Ubuntu并迁移数据,在此之前简要的学习Git的上传下载操作。 目录 🕒 1. 连接🕘 1.1 配…

路由策略原理与配置

🐣个人主页 可惜已不在 🐤这篇在这个专栏 华为_可惜已不在的博客-CSDN博客 🐥有用的话就留下一个三连吧😼 目录 一. 原理概述 二. 实验目的 实验内容 实验拓扑 实验配置 三. 实验结果 一. 原理概述 路由策略Route-P…

【docker】docker 关键技术 —— 镜像制作

docker 镜像制作 镜像制作及原因Docker 镜像制作方式快照方式制作镜像制作命令 Dockerfile 制作镜像Dockerfile 是什么Dockerfile 格式为什么需要 Dockerfilegitee 详细使用 Dockerfile 教程 镜像制作及原因 镜像制作是因为某种需求,官方的镜像无法满足需求&#x…

Docker基本使用(持续更新中)

1 常用命令 1.1保存镜像到本地 命令如下: docker save -o nginx.tar nginx:latest 举例 结果:在当前目录下多了一个nginx.tar的包 1.2加载本地镜像 命令如下: docker load -i nginx.tar 举例: 查看当前镜像是没有nginx的 加载本地镜…

C++入门基础知识67(高级)——【关于C++ 文件和流】

成长路上不孤单😊😊😊😊😊😊 【14后😊///C爱好者😊///持续分享所学😊///如有需要欢迎收藏转发///😊】 今日分享关于C 文件和流的相关内容! 关于…

数据库索引底层数据结构之B+树MySQL中的页索引分类【纯理论干货,面试必备】

目录 1、索引简介 1.1 什么是索引 1.2 使用索引的原因 2、索引中数据结构的设计 —— B树 2.1 哈希 2.2 二叉搜索树 2.3 B树 2.4 最终选择之——B树 2.4.1 B树与B树的对比(面向索引)【面试题】 3、MySQL中的页 3.1 页的使用原因 3.2 页的结构 3.2.1 页文件头和页文件…

【批量图片发票识别表格】批量识别发票明细导出Excel表格,批量识别扫描发票,批量识别拍照发票,发票识别改名

我们在生活中有很多发票要处理,有的是扫描的图片,有的是拍照的图片,需要将这些发票的信息导出整理成Excel表格,手打一两张还可以,数量大是不太现实的,那么今天教下大家如何快速的将这些发票整理下Excel表格…

基于Java、SpringBoot、Vue的加油站管理系统设计

摘要 本系统是一个基于Java、SpringBoot和Vue的加油站管理系统。它旨在提高加油站的运营效率,优化客户服务体验,并通过数据分析支持更精准的业务决策。该系统包括用户管理、汽油管理、站点管理等功能模块。通过这些功能,管理员可以方便地管理…

Chrome扩展程序上架全流程

1. 开发一个扩展 首先开发好一个扩展, 在构建之后压缩打包为 zip 格式的文件 2. 注册开发者账号 首次需要创建谷歌开发者账号,需要一次性收取5美元的注册费 注册步骤 打开注册链接,如下图所示,同意协议点击支付注册费 需要…

GDPU Vue前端框架开发 计数器

计数器算不到你双向绑定的进度。 重要的更新公告 !!!GDPU的小伙伴,感谢大家的支持,希望到此一游的帅哥美女能有所帮助。本学期的前端框架及移动应用,采用专栏订阅量达到50才开始周更了哦( •̀ .̫ •́ )✧…

一步到位:通过 Docker Compose 部署 EFK 进行 Docker 日志采集

一、EFK简介 Elasticsearch:一个开源的分布式搜索和分析引擎,用于存储和查询日志数据。它是 EFK 的核心组件,负责高效地存储和检索日志信息。 Filebeat:一个轻量级的日志采集器,主要用于将日志文件数据发送到 Logsta…

Python之NumPy超详细学习指南:从入门到精通(上篇)

文章目录 Python NumPy学习指南:从入门到精通第一部分:NumPy简介与安装1. 什么是NumPy?2. 安装NumPy使用pip安装:使用Anaconda安装: 第二部分:NumPy数组基础1. NumPy数组的创建从列表创建一维数组&#xff…

第四天旅游线路预览——从贾登峪到喀纳斯景区入口(贾登峪游客服务中心)

第四天:从贾登峪到喀纳斯风景区入口,晚上住宿贾登峪; 从贾登峪到喀纳斯景区入口(贾登峪游客服务中心): 搭乘贾登峪①路车,路过三湾到达景区换乘中心,路程时长约40分钟; …

在服务器上开Juypter Lab教程(远程访问)

在服务器上开Juypter Lab教程(远程访问) 文章目录 在服务器上开Juypter Lab教程(远程访问)一、安装anaconda1、安装anaconda2、提权限3、运行4、同意协议5、安装6、是否要自动初始化 conda7、结束8、检查 二、Anaconda安装Pytorch…

jsp+sevlet+mysql图书管理系统

jspsevletmysql图书管理系统 一、系统介绍二、功能展示1.图书查询(学生)2.借阅信息(学生)3.借阅历史(学生)4.借阅历史(管理员)5.读者管理(管理员)6.图书分类(管理员)7.图书借阅信息(管理员)8.图书归还信息(管理员) 四、其它1.其他系统实现 一、系统介绍 系统主要功能&#xff…