当前位置: 首页 > news >正文

一文讲清楚React Fiber

文章目录

  • 一文讲清楚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.dtcms.com/a/268540.html

相关文章:

  • RAG 相关概念学习
  • VMware 17.0.2-21581411 安装教程(附详细步骤+序列号激活指南)
  • 【牛客算法】 小红的奇偶抽取
  • kotlin+MongoTemplate的时间类型为is_date类型 pymongo如何处理
  • 【vue】用conda配置nodejs,一键开通模版使用权
  • 设计模式分析
  • 1.1_5_1 计算机网络的性能指标(上)
  • 大模型在肾囊肿诊疗全流程预测及应用研究报告
  • kafka总结
  • 【Java编程动手学】Java常用工具类
  • Apache Cloudberry 亮相 2025 IvorySQL 生态大会暨 PostgreSQL 高峰论坛
  • c# Process.Start异常解决办法
  • 【一起来学AI大模型】支持向量机(SVM):核心算法深度解析
  • 支持向量机(SVM)在心脏MRI分类(心肌病检测)中的应用与实现
  • 最简单的实验室资产管理系统,使用Flask,mysql,html(四、知识补充)
  • C++学习笔记01(自学草稿)
  • 【用 Scapy 实现一个简单的局域网 MAC 地址扫描工具】
  • 20250707-2-第二章:Kubernetes 核心概念-K8s集群架构,生产部署K8s两_笔记
  • 环路滤波:精密ADC时钟系统的相位噪声净化器
  • 源码推送到gitee码云仓库
  • stm32--SPI原理应用W25Q64(二)
  • 国产时序数据库 TDengine:Docker 部署、协议端口及 DBeaver 连接全攻略
  • JVM系列五:字节码与执行引擎深度解析
  • uniapp运行项目到ios基座
  • WebRTC 双向视频通话
  • LeetCode 面试题 02.02. 返回倒数第 k 个节点
  • Java 大视界 -- Java 大数据在智能安防视频监控系统中的视频质量评估与智能修复(337)
  • Kettle + 大数据实战:从数据采集到分布式处理的完整流程指南
  • Kafka生产者的初始化
  • Angular V20 新特性