文章目录

  • 一文讲清楚React Fiber
  • 1. 基础概念
    • 1.1浏览器刷新率(帧)
    • 1.2 JS执行栈
    • 1.3 时间分片
    • 1.4 链表
  • 2. React Fiber是如何实现更新过程控制
    • 2.1 任务拆分
    • 2.2挂起、恢复、终止
      • 2.2.1 挂起
      • 2.2.2 恢复
      • 2.2.3 终止
    • 2.3 任务具备优先级

一文讲清楚React Fiber

1. 基础概念

1.1浏览器刷新率(帧)

  • 页面都是一帧一帧绘制出来的,浏览器大多是60Hz(60帧/s),每一帧耗时16ms左右,每一帧分为以下7个过程
    1. 接手输入事件
    1. 执行回调事件
    1. 开始一帧
    1. 执行RequestAnimationFrame,即RAF
    1. 页面布局,计算样式
    1. 渲染
    1. 执行RequestIdleCallback,即RIC
  • 其中,RIC事件并不是每一帧结束都会执行,只有在一帧的16ms内做完了前6件事切还有剩余时间,RIC才会执行。如果执行了RIC事件,那么下一帧就要在事件执行结束后才能继续渲染,所以RIC的执行时间不宜太长,不然浏览器得不到控制权,无法完成下一帧的渲染,会出现页面卡顿

1.2 JS执行栈

  • React16之前,是通过原生执行栈递归遍历DOM,会形成一个执行栈,每次更新浏览器会从栈顶开始执行,直到执行栈被清空才会把执行权交给浏览器。而在React中,页面视图都被视为一个个函数执行的结果,这就意味着有多个函数的调用。如果页面很复杂,执行栈就会很深,就要占据很长的一段时间,浏览器渲染就会停滞,就会出现卡顿等问题

1.3 时间分片

  • 就是将粒度很小的任务放入一个时间段(一帧)去执行的一种方案,React Fiber就是将多个任务放入一个时间分片去执行

1.4 链表

  • 链表的概念不用多少说
  • React16之后,使用多向链表代替了原来的树结构,同时还会生成副作用单链表和状态更新单链表

2. React Fiber是如何实现更新过程控制

  • 过程可控体现在三方面
    1. 任务拆分
    1. 任务挂起、恢复、终止
    1. 任务具备优先级

2.1 任务拆分

  • React Fiber 将遍历VDOM拆分成若干个小任务,每个人物只负责一个节点的处理

2.2挂起、恢复、终止

  • 在React Fiber架构中,更新过程的核心在于两棵Fiber树的协同工作:当前工作树(workInProgress)和当前渲染树(current)。这两棵树构成了React实现可中断渲染的基础架构。

  • 工作树(workInProgress)是React在执行更新时正在构建的新版本Fiber树。每当应用状态发生变化(如通过setState触发更新),React就会开始构建这棵新树。在构建过程中,每个Fiber节点都- 会记录自身的变更标记(effectTag),最终整棵树会形成完整的变更链表。

  • 当前树(current)则代表着上次渲染周期最终呈现的UI对应的Fiber结构。每次更新完成后,新构建的workInProgress树就会成为新的current树。在下一次更新开始时,React会基于这个current树- 创建新的workInProgress树,并通过alternate指针在两树的对应节点间建立关联。

  • 在构建新workInProgress树的过程中,React会执行关键的协调算法:

  • 通过对比新旧节点(diff算法)来确定需要应用的变更

  • 尽可能复用current树中的节点实例,避免不必要的对象创建

  • 为每个节点标记具体的更新类型(如新增、修改或删除)

  • 整个更新过程本质上就是workInProgress树的渐进式构建过程:

  • React会将构建任务分解为多个工作单元

  • 每个工作单元完成后可以暂停让出主线程

  • 通过循环调度机制继续处理下一个工作单元

  • 这种分片执行方式使得高优先级更新可以中断低优先级任务

  • 这种双树机制赋予了React三大核心能力:

  • 可中断的渐进式渲染

  • 更新优先级的智能调度

  • 高效的节点复用策略

  • 值得注意的是,所有与任务调度相关的操作(暂停、恢复或取消)都发生在workInProgress树的构建阶段。React通过这种巧妙的架构设计,在保持声明式编程模型的同时,实现了接近原生渲染的性能表现。

2.2.1 挂起

  • 当第一个小任务完成后,先判断这一帧是否还有空闲时间,没有就挂起下一个任务的执行,记住当前挂起的节点,让出控制权给浏览器执行更高优先级的任务。

2.2.2 恢复

  • 在浏览器渲染完一帧后,判断当前帧是否有剩余时间,如果有就恢复执行之前挂起的任务。如果没有任务需要处理,代表调和阶段完成,可以开始进入渲染阶段。这样完美的解决了调和过程一直占用主线程的问题。
    那么问题来了他是如何判断一帧是否有空闲时间的呢?答案就是我们前面提到的 RIC (RequestIdleCallback) 浏览器原生 API,React 源码中为了兼容低版本的浏览器,对该方法进行了 Polyfill。

当恢复执行的时候又是如何知道下一个任务是什么呢?答案在前面提到的链表。在 React Fiber 中每个任务其实就是在处理一个 FiberNode 对象,然后又生成下一个任务需要处理的 FiberNode

class FiberNode {constructor(tag, pendingProps, key, mode) {// 实例属性this.tag = tag; // 标记不同组件类型,如函数组件、类组件、文本、原生组件...this.key = key; // react 元素上的 key 就是 jsx 上写的那个 key ,也就是最终 ReactElement 上的this.elementType = null; // createElement的第一个参数,ReactElement 上的 typethis.type = null; // 表示fiber的真实类型 ,elementType 基本一样,在使用了懒加载之类的功能时可能会不一样this.stateNode = null; // 实例对象,比如 class 组件 new 完后就挂载在这个属性上面,如果是RootFiber,那么它上面挂的是 FiberRoot,如果是原生节点就是 dom 对象// fiberthis.return = null; // 父节点,指向上一个 fiberthis.child = null; // 子节点,指向自身下面的第一个 fiberthis.sibling = null; // 兄弟组件, 指向一个兄弟节点this.index = 0; //  一般如果没有兄弟节点的话是0 当某个父节点下的子节点是数组类型的时候会给每个子节点一个 index,index 和 key 要一起做 diffthis.ref = null; // reactElement 上的 ref 属性this.pendingProps = pendingProps; // 新的 propsthis.memoizedProps = null; // 旧的 propsthis.updateQueue = null; // fiber 上的更新队列执行一次 setState 就会往这个属性上挂一个新的更新, 每条更新最终会形成一个链表结构,最后做批量更新this.memoizedState = null; // 对应  memoizedProps,上次渲染的 state,相当于当前的 state,理解成 prev 和 next 的关系this.mode = mode; // 表示当前组件下的子组件的渲染方式// effectsthis.effectTag = NoEffect; // 表示当前 fiber 要进行何种更新this.nextEffect = null; // 指向下个需要更新的fiberthis.firstEffect = null; // 指向所有子节点里,需要更新的 fiber 里的第一个this.lastEffect = null; // 指向所有子节点中需要更新的 fiber 的最后一个this.expirationTime = NoWork; // 过期时间,代表任务在未来的哪个时间点应该被完成this.childExpirationTime = NoWork; // child 过期时间this.alternate = null; // current 树和 workInprogress 树之间的相互引用}
}

2.2.3 终止

  • 其实并不是每次更新都会走到提交阶段。当在调和过程中触发了新的更新,在执行下一个任务的时候,判断是否有优先级更高的执行任务,如果有就终止原来将要执行的任务,开始新的 workInProgressFiber 树构建过程,开始新的更新流程。这样可以避免重复更新操作。这也是在 React 16 以后生命周期函数 componentWillMount 有可能会执行多次的原因

2.3 任务具备优先级

  • React Fiber 除了通过挂起,恢复和终止来控制更新外,还给每个任务分配了优先级。具体点就是在创建或者更新 FiberNode 的时候,通过算法给每个任务分配一个到期时间(expirationTime)。在每个任务执行的时候除了判断剩余时间,如果当前处理节点已经过期,那么无论现在是否有空闲时间都必须执行改任务
  • 同时过期时间的大小还代表着任务的优先级。
    任务在执行过程中顺便收集了每个 FiberNode 的副作用,将有副作用的节点通过 firstEffect、lastEffect、nextEffect 形成一条副作用单链表

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

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

相关文章

(3)机器学习小白入门 YOLOv: 解锁图片分类新技能

(1)机器学习小白入门YOLOv :从概念到实践 (2)机器学习小白入门 YOLOv:从模块优化到工程部署 (3)机器学习小白入门 YOLOv: 解锁图片分类新技能 前言 YOLOv 算法通常被用于目标检测任务,但通过对其进行适当的调整和改造&#xff0c…

主机安全-开源HIDS字节跳动Elkeid使用

安装好elkeid后就开始接入主机和k8s集群,安装文档-----主机安全-开源HIDS字节跳动Elkeid安装-CSDN博客 1、接入主机 在系统管理-----安装配置-----复制命令------在目标机器上执行这段命令 执行成功后主机就会自动接入 2、接入k8s集群 在k8s主机上执行脚本&#x…

【vue】用conda配置nodejs,一键开通模版使用权

特此鸣谢我的好同学重中之重的特级教学,非常之好用一、conda环境下载安装二、创建包含nodejs的conda环境创建一个新环境:conda create -n 【自定义环境名字】 python3.9 conda create -n my_nodejs_env python3.9激活新环境:conda activate【…

深度学习--tensor(创建、属性)

一、torch概念1.1简介pytorch简称torch,意为深度学习框架。它使用张量(tensor)来表示数据,可以轻松地处理大规模数据集,且可以在GPU上加速。pytorch基本功能:自动微分、自动求导等。1.2安装官网获得下载命令…

【内存】Linux 内核优化实战 - net.ipv4.tcp_max_tw_buckets

目录net.ipv4.tcp_max_tw_buckets 详解一、基本概念二、核心作用三、默认值四、调整场景需增大参数的场景需减小参数的场景五、查看与修改方法1. 查看当前值2. 临时修改(重启失效)3. 永久修改(重启生效)六、注意事项总结net.ipv4.…

短剧系统开发定制全流程解析:从需求分析到上线运营

一、短剧行业现状与系统开发价值短剧作为一种新兴的内容形态,近年来呈现爆发式增长态势。2023年中国短剧市场规模已突破300亿元,用户规模超过5亿,这种以"快节奏、强剧情、低成本"为特点的内容形式正在重塑数字娱乐产业格局。短剧系…

各服务器厂商调整BIOS睿频教程

调整BIOS睿频选项汇总:1、华为服务器:2、华为服务器V53、浪潮服务器4、浪潮服务器M45、 曙光服务器5.1 曙光I620-G205.2 曙光I620-G306、联想服务器(650系列)650系列的服务器对照截图信息修改对应项,修改为截图里的选项…

PyTorch笔记3----------统计学相关函数

1.基础函数 import torch a torch.rand(2,2) print("a:\n",a) print(########################) print("平均值:\n",torch.mean(a,dim0)) print("总和:\n",torch.sum(a,dim0)) print("所有元素的积:\n",torch.prod(a,dim0)) print(&…

【Prometheus】通过tar包部署单机版Prometheus 和 Pushgateway

在ECS(Elastic Compute Service)机器上通过tar包部署 Prometheus 和 Pushgateway,并配置 Prometheus 采集 Pushgateway 的数据,是一个常见的监控部署任务。以下是详细的步骤说明:🧩 环境准备 操作系统&…

Matlab 频谱分析 (Spectral Analysis)

文章目录1. 信号预处理 - 去直流分量2. 快速傅里叶变换(FFT)3. 功率谱密度(PSD)计算4. 主频率检测5. 谱质心计算6. 对数谱显示完整的信号处理流程实际应用示例1. 信号预处理 - 去直流分量 data data - mean(data);数学原理&…

【实时Linux实战系列】实时以太网与 TSN 基础

在实时系统中,网络通信的实时性和可靠性是确保系统正常运行的关键。实时以太网和时间敏感网络(TSN)技术为实时数据传输提供了强大的支持。TSN通过一系列协议和机制,确保数据能够在预定的时间内可靠传输,满足工业自动化…

茶颜悦色JAVA面试分享

1、自我介绍项目2、设计一个爆款饮品秒杀系统:如何解决“幽兰拿铁”上新时的瞬时10万QPS?从缓存、限流、库存扣减到订单创建的全流程设计。3、订单超市未支付自动取消:如何实现高精度(30分钟精确到秒)且低延迟的订单状…

OneCode图表配置速查手册

前言 在数据可视化日益成为业务决策核心驱动力的今天,高效、灵活的图表配置系统已成为开发人员不可或缺的工具。OneCode图表组件凭借其丰富的图表类型与精细化的配置能力,为开发者提供了构建专业数据可视化界面的完整解决方案。然而,随着图表…

二维码驱动的独立站视频集成方案

一、独立站视频嵌入的技术挑战与架构设计 在独立站建设中,视频内容的集成面临着性能、安全与用户体验的三重挑战。传统直接嵌入方式会导致页面加载缓慢(平均增加3-5秒首屏时间)、服务器带宽消耗激增(单视频日均播放1000次约产生50…

【STM32】预分频因子(Prescaler)和重装载值(Reload Value)

在 STM32 的 独立看门狗(IWDG) 中(结合上文【STM32】独立看门狗(提供完整实例代码)),为了控制看门狗的超时时间(溢出时间),我们主要设置两个参数:…

从0到1搭建同城O2O外卖平台:外卖系统源码架构解析与实战指南

当下,越来越多的创业者、品牌连锁商家,甚至社区集群,开始布局属于自己的本地外卖平台。而对于软件开发者和技术团队而言,如何从0到1搭建一套可落地、可扩展、可持续运营的外卖系统,成为了一个既现实又挑战性十足的话题…

MySQL 8.0 OCP 1Z0-908 题目解析(16)

题目61 Choose the best answer. Examine this command, which executes successfully: mysqlbackup --defaults-file/backups/server-my.cnf --backup-dir/backups/full copy-backWhich statement is true about the copy-back process? ○ A) It restores files from the da…

WSL命令

以下是 WSL&#xff08;Windows Subsystem for Linux&#xff09;的常用命令大全&#xff0c;涵盖安装、管理、网络、文件交互等场景&#xff0c;方便快速查阅和使用&#xff1a;1. 安装与版本管理命令说明wsl --install默认安装 WSL 和 Ubuntuwsl --install -d <发行版名&g…

AI语音训练——GPT-SoVITS(GSV)

链接说明 github项目地址&#xff1a;RVC-Boss/GPT-SoVITS: 1 min voice data can also be used to train a good TTS model! (few shot voice cloning) 项目中文说明书&#xff1a; GPT-SoVITS指南//项目说明书里也有在线使用的链接 原项目作者B站教学视频&#xff1a;耗时两个…

事件委托版本tab栏切换

事件委托&#xff1a;是JavaScript中注册事件的常用技巧&#xff0c;也称事件委派、事件代理简单理解&#xff1a;原本需要注册在子元素的事件委托给父元素&#xff0c;让父元素担当事件监听的职务优点&#xff1a;减少注册次数&#xff0c;可提高程序性能原理&#xff1a;事件…