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

React Fiber 解析:前端性能提升密码

文章目录

    • 背景
    • React 采用 fiber 主要为了解决哪些问题?
      • 性能问题:
      • 用户体验问题:
    • 为什么在 React 15 版本中性能会差:
      • 浏览器绘制原理:
      • react 15 架构和问题
    • 那么 fiber 怎么解决了这个问题?
      • 任务“大”的问题
      • 递归、可中断的问题
      • 调度的问题
      • fiber 架构
      • fiberNode 数据结构
    • 总结
    • 问题
      • fiber 数据结构为什么必须是链表,数组不行吗?

背景

深入学习 react 肯定脱离不了对 fiber 的研究,因此这篇文章记录下 fiber 到底是什么?

React 采用 fiber 主要为了解决哪些问题?

性能问题:

  • 在 React 早期版本中,当渲染大量的 DOM 节点时,由于其同步渲染机制,会导致整个渲染过程阻塞浏览器主线程,使得用户界面变得不响应,甚至出现丢帧的问题。当状态更新导致组件重新渲染时,React 需要重新构建整个虚拟 DOM 树,并与之前的虚拟 DOM 树进行比较以找出差异(diff),然后将这些差异应用到实际的 DOM 上。如果这个过程非常耗时,那么在渲染期间用户将无法与应用交互。

用户体验问题:

用户期望现代 Web 应用能够快速响应他们的操作。然而,在旧版 React 中,如果某个部分正在重新渲染,那么这段时间内用户无法得到任何反馈,这降低了用户体验。
为了应对这些问题,React 16 引入了 Fiber 架构,它改变了 React 的核心算法,带来了以下改进:

  • 异步渲染:Fiber 允许 React 将渲染工作分割成更小的片段,并且可以暂停、恢复这些片段,从而使得渲染工作不再是阻塞式的。这样,即使在渲染过程中,也可以让出执行权给浏览器去处理其他任务,如事件处理和绘制。
  • 优先级调度:通过引入优先级的概念,React 可以根据不同的情况决定哪些更新应该优先完成。例如,用户的直接交互通常会被赋予更高的优先级,而一些后台的更新则可以延迟。
  • 增量渲染:Fiber 还支持增量提交,这意味着 React 可以分批次地将更改应用到真实 DOM 上,而不是一次性地进行大规模的 DOM 更新,这样可以减少浏览器重排和重绘的成本。

为什么在 React 15 版本中性能会差:

浏览器绘制原理:

大家都知道,浏览器是多进程多线程的,主要进程包括主进程、渲染进程、插件进程和 GPU 进程。本次我们主要关注渲染进程,这是页面渲染、HTML 解析、CSS 解析和 JS 执行的地方。

在渲染进程中,有两个核心线程:

  • GUI 线程:负责浏览器界面的渲染,包括解析 HTML、CSS 以及布局绘制。
  • JS 线程:包含我们编写的 JS 代码的解析引擎,最著名的是 Google 的 V8。
    需要注意的是,JS 引擎和 GUI 渲染是互斥的。JS 可能会更改 HTML 或 CSS 样式,如果同时执行会导致页面渲染混乱,因此当 JS 引擎执行时,GUI 渲染线程会被挂起,直到 JS 引擎执行完毕。

我们通常看到的动画和视频本质上是通过快速切换图片来欺骗人眼,让人感觉是连续的动画。每秒内的图片越多,动画越流畅,通常 60 帧每秒(FPS)就能让人感觉流畅。因此,Chrome 需要在 16 ms 内完成渲染任务,以避免掉帧。如果JS执行时间过长,用户会感觉到明显的卡顿,很影响用户体验。

react 15 架构和问题

React 15 的架构主要分为 Reconciler(协调器) 和 Renderer(渲染器)。

  • Reconciler(协调器),VDOM 、diff,协调器主要负责根据自变量变化计算出UI变化。
  • Renderer(渲染器), 渲染器主要负责把UI变化渲染到宿主环境中。

Reconciler 采用递归的方式创建、更新 vdom,递归一旦开始则不可中断和停止,如果执行比较复杂的深层次的 vdom 递归运算,则会导致 Reconciler 暂用大量时间,无法进行 Renderer 则会导致页面卡顿。

那么 fiber 怎么解决了这个问题?

首先我们先把问题抽象一下,不要带入 react 框架中,放入我们日常业务场景中,问题就可以转化为 :

一个比较大的任务,一次性处理起来耗时很长,需要我们去优化。

既然一次性处理耗时太长,那么能不能分多次处理,可不可以只在浏览器空闲的时候处理,繁忙的时候先暂定处理。

任务“大”的问题

大任务的处理其实在日常开发中我们应该经常会遇到如:“大文件上传、加载”,“批量请求并发”等场景。我们经常会用到切片的思想,把一个大的任务拆分成一个个小任务按照一定顺序执行,这样我们就可以减少大任务的执行时间,让浏览器有 breathing time,让用户有更好的体验。

替换到我们的场景中,vdom 就是我们的”大任务“,然后我们要切分成每一个小任务就是 fiber 节点,每一个 fiber 节点关联起来就组成我们的 fiber 树。

递归、可中断的问题

解决了“大”的问题接下来就是递归的问题,之前 diff 是递归的不可中断,那我们怎么才能做到可中断呢?

原本 vdom 是一个树结构想要遍历只能采取递归的方式,我们要舍弃递归只能把树结构转化为另一种数据结构,react 采用了链表把每个 fiber 关联起来,然后进行遍历。然后再中断的时候记录下来当前指针,恢复时可以继续执行。

调度的问题

解决了“递归”的问题接下来就是调度的问题,我们怎么才能在浏览器空闲时间去调用呢?

这边引入一个比较新鲜的 api requestIdleCallback 这个方法插入一个函数,这个函数将在浏览器空闲时期被调用。这使开发者能够在主事件循环上执行后台和低优先级工作,而不会影响延迟关键事件。这个 api 刚好就很契合我们的场景可以在浏览器空闲的时候去调用,我们执行某个 fiberNode 的时候,浏览器主线程被占用,这个时候就可以暂停 fiberNode 的继续执行,等浏览器空闲时,继续 nextUnitOfWork。这就实现了可暂停可继续。但是呢这 api 有限制:

  • 兼容性问题,有的浏览器不支持这个 API。
  • 这个方法任务执行优先级无法自定义,且并不符合 react 团队的预期

总之,react 最后自己封装了一个 Scheduler,提供更强大的事件优先级处理逻辑,取代了 idle 的功能。具体执行策略可以参考一下其他文章。

fiber 架构

有了这些理论思想的加持,我们再看一下 fiber 架构。

  • Scheduler(调度器): 时间切片、调度任务的优先级
  • Reconciler(协调器),diff 过程,协调器主要负责根据自变量变化计算出UI变化。
  • Renderer(渲染器), 渲染器主要负责把UI变化渲染到宿主环境中。

相比我们的 react 15 多了调度器的概念,这里调度器其实就是我们上面提到的时间分片机制,控制我们的 workLoop。

fiberNode 数据结构

function Counter() {const [state, setState] = React.useState(1);return (<h1 onClick={() => setState(state + 1)}>Count: {state}</h1>);
}
//  简单的 vdom 结构
{type: 'h1',props: {children: [{type: 'text',props: {"nodeValue": "Count",children: []}},{type: 'text',props: {"nodeValue": "1",children: []}}]}
}

可以看到这是之前的 vdom 结构, 我们需要转化为 fiber 结构,按照我们上面切片的思想,元素结构需可以拆分成3个部分 h1 count:state,得到如下3个片段:

const fiber1 = {type: 'h1',props: {children: []}
}
const fiber2 = {type: 'text',props: {"nodeValue": "Count",children: []}
}
const fiber3 = {type: 'text',props: {"nodeValue": "1",children: []}
}

那怎么表示之间的关联关系呢? 我们这边可以通过链表的方式相互关联:

  • 父 -> 子: children
  • 子 -> 父: return
  • 兄 -> 弟: sibling

转化为如下结构(基础版的 fiber):

fiber1const fiber1 = {type: 'h1',props: {children: fiber2}
}
const fiber2 = {type: 'text',props: {"nodeValue": "Count",children: null},sibling: fiber3,return : fiber1
}
const fiber3 = {type: 'text',props: {"nodeValue": "1",children: null},sibling: null,return : fiber1
}

现在我们已经得到了一个 fiber 的基本结构,以下为 react 源码中的 fiber 结构

// react/packages/react-reconciler/src/ReactInternalTypes.js
export type Fiber = {|tag: WorkTag,key: null | string,elementType: any,type: any,stateNode: any,return: Fiber | null,child: Fiber | null,sibling: Fiber | null,index: number,ref:| null| (((handle: mixed) => void) & {_stringRef: ?string, ...})| RefObject,pendingProps: any,memoizedProps: any, // 状态updateQueue: mixed,memoizedState: any, // hooksdependencies: Dependencies | null,mode: TypeOfMode,flags: Flags,subtreeFlags: Flags,deletions: Array<Fiber> | null,nextEffect: Fiber | null,firstEffect: Fiber | null,lastEffect: Fiber | null,lanes: Lanes, // 优先级相关childLanes: Lanes, // 优先级相关alternate: Fiber | null, // 双缓存actualDuration?: number,actualStartTime?: number,selfBaseDuration?: number,treeBaseDuration?: number,_debugID?: number,_debugSource?: Source | null,_debugOwner?: Fiber | null,_debugIsCurrentlyTiming?: boolean,_debugNeedsRemount?: boolean,_debugHookTypes?: Array<HookType> | null,
|};

总结

最后,那 fiber 是什么?

fiber 是 react 用于提升性能和渲染效率的一种架构,同时也是一种贯穿在整个架构中运行的数据结构。

在日常业务开发中,可能会觉得框架源码层面的东西距离我们很遥远,但是当我们看透本质其实会发现这些问题我们也遇到过,复杂问题抛开原有特殊属性是不是就可以剥离成共性简单问题,这或许也是我们学习源码的意义。

问题

fiber 数据结构为什么必须是链表,数组不行吗?

fiber 采用链表数据结构的原因是因为链表可以方便地在列表的中间插入和删除元素。这在构建和更新用户界面时非常有用,因为可能会有大量的元素需要插入或删除。与数组相比,链表具有更好的插入和删除性能,因为在数组中执行这些操作通常需要移动大量元素,而在链表中只需要修改一些指针即可。

链表缺点:然而,链表的查找性能通常比数组差,因为需要遍历整个列表才能找到所需的元素。

尽管如此,fiber 还是选择使用链表作为其数据结构,因为在构建和更新用户界面时插入和删除元素的需求要远远大于查找元素的需求。

相关文章:

React Fiber 解析:前端性能提升密码

文章目录 背景React 采用 fiber 主要为了解决哪些问题&#xff1f;性能问题&#xff1a;用户体验问题&#xff1a; 为什么在 React 15 版本中性能会差&#xff1a;浏览器绘制原理&#xff1a;react 15 架构和问题 那么 fiber 怎么解决了这个问题&#xff1f;任务“大”的问题递…...

【吊打面试官系列-微服务面试题】微服务架构如何运作?

大家好&#xff0c;我是锋哥。今天分享关于【微服务架构如何运作&#xff1f;】面试题&#xff0c;希望对大家有帮助&#xff1b; 微服务架构如何运作&#xff1f; 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 微服务架构是一种将单一应用程序构建为一组小型、独…...

Windows系统编程 - 目录操作、磁盘、卷信息

前言 各位师傅大家好&#xff0c;我是qmx_07&#xff0c;今天继续讲解Windows系统编程的相关知识:目录操作 目录 目录操作 创建目录 通过CreateDirectory函数创建目录 原型: BOOL CreateDirectoryA([in] LPCSTR lpPathName,//目录名称[in, opt…...

搭建SaaS知识库:优化教育机构的在线教学效能

随着信息技术的飞速发展&#xff0c;教育领域正经历着前所未有的变革。在线教学已成为教育机构提供灵活学习体验、扩大覆盖范围、提升教学效率的重要手段。然而&#xff0c;如何在海量资源与复杂教学场景中高效管理知识&#xff0c;确保教学质量&#xff0c;成为教育机构面临的…...

CSS中backdrop-filter详解

文章目录 CSS中backdrop-filter详解一、引言二、backdrop-filter基础1、基本概念1.1、基本语法 2、滤镜函数2.1、代码示例 三、实际应用1、创建模糊背景1.1、代码示例 2、结合其他CSS属性2.1、代码示例 四、总结 CSS中backdrop-filter详解 一、引言 在现代网页设计中&#xf…...

AI测试入门:理解 LLM 的基准测试(Benchmark)

AI测试入门:理解 LLM 的基准测试(Benchmark) 1. 基准测试的定义2. 基准测试的目的3. 基准测试的常用指标4. 基准测试的流程5. 常用的AI基准测试框架总结1. 基准测试的定义 LLM 的基准测试是一种评估 LLM 的标准化方法,通过使用预定义的数据集、任务和评估指标,对LLM 在特定…...

InternVid:用于多模态视频理解与生成的大规模视频-文本数据集 | ICLR Spotlight

InternVid 是一个开源的大规模视频-文本数据集&#xff0c;旨在促进视频理解和生成任务的发展&#xff0c;由上海人工智能实验室与南京大学、中国科学院等单位联合发布&#xff0c;相关的工作已经被ICLR2024接收。它包含超过 700 万个视频&#xff0c;总时长近 76 万小时&#…...

Hive数仓操作(十)

一、Hive 分页查询 在大数据处理中&#xff0c;分页查询是非常常见的需求。Hive 提供了 LIMIT 和 OFFSET 关键字来方便地进行分页操作。本文将详细介绍它们的用法。 1. 基本用法 LIMIT&#xff1a;用于限制查询结果的行数。OFFSET&#xff1a;用于指定从哪一行开始检索。 2…...

Android 扩大View的点击区域

文章目录 Android 扩大View的点击区域使用padding属性使用TouchDelegate使用getLocationOnScreen监听 Android 扩大View的点击区域 使用padding属性 通过设置 padding 属性扩大点击区域。 使用&#xff1a; <?xml version"1.0" encoding"utf-8"?&…...

[Qt学习笔记] 解决QTextEdit数据过多UI卡死问题

背景问题 在项目中使用QTextEdit显示软件的日志信息&#xff0c;由于在连续输出日志信息&#xff0c;刚开始QTextEdit显示没什么问题&#xff0c;长时间就会出现UI界面卡死&#xff0c;内存占用变高。晚上查了说QTextEdit的append函数如果不释放会累计增加内存&#xff0c;包括…...

OgreNext高级材质中增加线宽,点大小,虚线模式绘制支持

修改Ogre高级材质系统&#xff0c;增加线宽&#xff0c;点大小&#xff0c;虚线模式&#xff0c;虚线参数的支持,效果如下&#xff1a; 需要修改的代码文件如下&#xff1a; 修改如下 代码文本&#xff1a; //范围[0.2 - 51] 0.2 * [0,255];Ogre::uint8 mLineWidth;//范围[…...

STM32中的DMA数据转运——下篇

STM32中的DMA数据转运——上篇-CSDN博客 在上篇文章中&#xff0c;我们讨论了STM32中的DMA&#xff08;直接存储器访问&#xff09;及其工作原理、存储器类型和总线设计。接下来&#xff0c;我们将更深入地探讨DMA的具体配置方法、常见应用场景以及一些实际设计中的注意事项。…...

51单片机的智能小区安防系统【proteus仿真+程序+报告+原理图+演示视频】

1、主要功能 该系统由AT89C51/STC89C52单片机LCD1602显示模块时钟模块温度传感器烟雾传感器CO传感器红外感应传感器IC卡蓝牙继电器按键、蜂鸣器、LED等模块构成。适用于智能小区安防、智能家居安防等相似项目。 可实现功能: 1、LCD1602实时显示北京时间、温度、烟雾浓度和CO浓…...

数仓建模流程

数仓建模简介 一句话总结 数仓建模中的“建模”是一个将数据有序组织和存储起来的过程&#xff0c;旨在提高数据的使用效率和降低使用成本。 详细描述 在数仓建模中&#xff0c;“建模”指的是构建数据模型&#xff0c;也就是数据的组织和存储方法。数据模型强调从业务、数…...

Neo4j CQL语句 使用教程

CREATE命令 : CREATE (<node-name>:<label-name>{ <Property1-name>:<Property1-Value>........<Propertyn-name>:<Propertyn-Value>} )字段说明 CREATE (dept:Dept { deptno:10,dname:“Accounting”,location:“Hyderabad” })&#…...

STM32-HAL库 驱动DS18B20温度传感器 -- 2024.10.8

目录 一、教程简介 二、驱动理论讲解 三、CubeMX生成底层代码 四、Keil5编写代码 五、实验结果 一、教程简介 本教程面向初学者&#xff0c;只介绍DS18B20的常用功能&#xff0c;但也能满足大部分的运用需求。跟着本教程操作&#xff0c;可在10分钟内解决DS18b20通信难题。…...

HTML 符号

HTML 符号 HTML(超文本标记语言)是一种用于创建网页的标准标记语言。它使用一系列的标签来描述网页的结构和内容。HTML 符号,通常指的是 HTML 标签,是构成 HTML 文档的基础。本文将详细介绍 HTML 符号的概念、种类、用途以及如何在网页设计中正确使用它们。 HTML 符号的概…...

编译后的MySQL安装

MySQL安装 1.下载网址2.下载方式3.配置配置环境变量修改配置文件初始化安装服务启动服务测试修改 Mysql 默认密码 1.下载网址 https://dev.mysql.com/downloads/mysql/2.下载方式 选择对应版本下载 mysql-xxx-winx64.zip&#xff0c;该压缩包为编译后文件&#xff0c;并非源码…...

Ubuntu安装Apache教程

系统版本&#xff1a;Ubuntu版本 23.04 Ubuntu是一款功能强大且用户友好的操作系统&#xff0c;而Apache是一款广泛使用的Web服务器软件。在Ubuntu上安装Apache可以帮助用户搭建自己的网站或者进行Web开发。为大家介绍如何在Ubuntu上安装Apache&#xff0c;并提供详细的教程和操…...

Nginx跳转模块之location与rewrite

目录 一、location模块与rewrite模块区别 二、location模块的基本介绍 1. location模块是什么&#xff1f; 2. 三种匹配类别 3. 常用的匹配规则 4. 匹配优先级 三、location模块使用实例 1.精准匹配优先级小于一般匹配的特殊情况 2 .解决方法 3. 实际网站使用中的三个匹配…...

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...

Python|GIF 解析与构建(5):手搓截屏和帧率控制

目录 Python&#xff5c;GIF 解析与构建&#xff08;5&#xff09;&#xff1a;手搓截屏和帧率控制 一、引言 二、技术实现&#xff1a;手搓截屏模块 2.1 核心原理 2.2 代码解析&#xff1a;ScreenshotData类 2.2.1 截图函数&#xff1a;capture_screen 三、技术实现&…...

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...

Ubuntu系统下交叉编译openssl

一、参考资料 OpenSSL&&libcurl库的交叉编译 - hesetone - 博客园 二、准备工作 1. 编译环境 宿主机&#xff1a;Ubuntu 20.04.6 LTSHost&#xff1a;ARM32位交叉编译器&#xff1a;arm-linux-gnueabihf-gcc-11.1.0 2. 设置交叉编译工具链 在交叉编译之前&#x…...

微服务商城-商品微服务

数据表 CREATE TABLE product (id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 商品id,cateid smallint(6) UNSIGNED NOT NULL DEFAULT 0 COMMENT 类别Id,name varchar(100) NOT NULL DEFAULT COMMENT 商品名称,subtitle varchar(200) NOT NULL DEFAULT COMMENT 商…...

智能分布式爬虫的数据处理流水线优化:基于深度强化学习的数据质量控制

在数字化浪潮席卷全球的今天&#xff0c;数据已成为企业和研究机构的核心资产。智能分布式爬虫作为高效的数据采集工具&#xff0c;在大规模数据获取中发挥着关键作用。然而&#xff0c;传统的数据处理流水线在面对复杂多变的网络环境和海量异构数据时&#xff0c;常出现数据质…...

Python ROS2【机器人中间件框架】 简介

销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

打手机检测算法AI智能分析网关V4守护公共/工业/医疗等多场景安全应用

一、方案背景​ 在现代生产与生活场景中&#xff0c;如工厂高危作业区、医院手术室、公共场景等&#xff0c;人员违规打手机的行为潜藏着巨大风险。传统依靠人工巡查的监管方式&#xff0c;存在效率低、覆盖面不足、判断主观性强等问题&#xff0c;难以满足对人员打手机行为精…...

uniapp 集成腾讯云 IM 富媒体消息(地理位置/文件)

UniApp 集成腾讯云 IM 富媒体消息全攻略&#xff08;地理位置/文件&#xff09; 一、功能实现原理 腾讯云 IM 通过 消息扩展机制 支持富媒体类型&#xff0c;核心实现方式&#xff1a; 标准消息类型&#xff1a;直接使用 SDK 内置类型&#xff08;文件、图片等&#xff09;自…...