从0到1,带你深入了解react fiber

news2025/1/11 17:03:18

react16之后,react引入了fiber架构,那么它究竟是什么,如何实现的呢?下面就让笔者带你掰扯掰扯,如有错误,欢迎指正

 

目录

渲染过程

react15

react16

为什么要引入fiber

不可中断原因

fiber详解

是什么

为什么使用链表这种数据结构

结构

具体过程

1.调度阶段(schedule)

2.协调阶段(reconclie)

两颗fiberTree

workInprogressTree的构建

3.渲染阶段(commit)


渲染过程

想要深入了解fiber,我们就要先弄清楚react页面渲染的大致流程。

当调用render(初次渲染页面)或者setState(数据更新导致页面重新渲染)时候,会引发页面的渲染。

react15

react15把页面渲染分为了两个阶段:

1.协调/调和阶段(reconclie):在这个阶段 React 会更新数据生成新的 虚拟Dom树,然后通过Diff算法,递归整个(注意是整个!)虚拟Dom树找出需要更新的节点,放到更新队列中去,得到新的更新队列。该过程不可中断

2.渲染阶段(commit):这个阶段 React 会遍历更新队列,将其所有的变更一次性更新到Dom上。

简单来说,一个负责找,另一个负责改。

react16

react16引入了fiber的概念,此时的页面渲染可以分为三个阶段:

1.调度阶段(schedule):调度任务的优先级,高优先级的先进入协调阶段

2.协调阶段(reconclie):找出变化的节点,从不可中断的递归变成了可中断的循环过程,内部采用了fiber架构,react16就是把之前的stack reconlie(直到执行栈被执行空才会停止)重构成了fiber reconclie(可中断)。

3.渲染阶段(commit):将变更一次性更新到真实Dom上。

为什么要引入fiber

上面我们将react15和react16页面渲染的大致流程进行了讲解,我们会发现,最直观的差异就是:寻找变化节点的过程由不可中断变成了可以中断

为什么要这样做呢?

浏览器js引擎页面渲染引擎是在同一个渲染线程之内,两者是互斥关系。

当前有新的数据更新时候,我们需要递归整个虚拟Dom树,找出变动节点,如果其中dom节点过多,那么这个过程时间消耗的会很长,并且无法中断,所以会导致其他事件影响滞后,造成卡顿

react15及之前版本:当有节点发生变动,会递归对比虚拟dom,找出变动节点,之后同步更新他们。这种遍历是递归调用,执行栈会越来越深,而且不能中断,中断后就不能恢复了。递归如果非常深,就会十分卡顿。

不可中断原因

react15中协调阶段不可中断原因:

简单来说,我们的Vnode(虚拟节点)中只存有子节点(childrenNode)的信息,如果我们打断了,是找不到它的父亲和兄弟节点的,所以就又得重头开始找。

fiber详解

是什么

fiber:直译 纤维,意在指比线程Thread更细小的执行粒度。

从整体上看,fiber就是把原本不可中断的协调过程碎片化,化整为零,每次执行完一个小任务,都会让出线程,给其他任务执行的机会。

从本质上看,fiber实际上是一种数据结构:特殊的链表。每个节点都是一个 fiberNode。一个 fiberNode包括了 child(第一个子节点)、sibling(兄弟节点)、return(父节点)等属性(解决了react15Vnode中只有子节点信息,信息不足导致无法中断的问题)。

react 会先把 vdom 转换成 fiber,再去进行 reconcile,这样就是可打断的了。

为什么使用链表这种数据结构

一句话总结就是:空间换时间

相较于原来react15中虚拟Dom的顺序结构数据格式,好处

1.操作更高效:调整指针指向即可。

2.有更多的信息:可以根据当前节点找到父节点、子节点、兄弟节点。

缺点:占用更多空间


结构

简单来说,fiberNode是虚拟dom节点经过处理之后生成的。fiberTree则是由fiberNode组成的。

fiber 节点结构如下:

{
    
    type: any, // 对于类组件,它指向构造函数;对于DOM元素,它指定HTML tag
    key: null | string, // 唯一标识符
    stateNode: any, // 保存对组件的类实例,DOM节点或与fiber节点关联的其他React元素类型的引用
    child: Fiber | null, // 大儿子
    sibling: Fiber | null, // 下一个兄弟
    return: Fiber | null, // 父节点
    tag: WorkTag, // 定义fiber操作的类型, 详见https://github.com/facebook/react/blob/master/packages/react-reconciler/src/ReactWorkTags.js
    nextEffect: Fiber | null, // 指向下一个节点的指针
    updateQueue: mixed, // 用于状态更新,回调函数,DOM更新的队列
    memoizedState: any, // 用于创建输出的fiber状态
    pendingProps: any, // 已从React元素中的新数据更新,并且需要应用于子组件或DOM元素的props
    memoizedProps: any, // 在前一次渲染期间用于创建输出的props
    // 还有很多,就不一一展示了
}

具体过程

1.调度阶段(schedule)

在这个阶段,会给每个任务一个优先级,具体点就是在创建或者更新 FiberNode 的时候,通过算法给每个任务分配一个到期时间(expirationTime)。在每个任务执行的时候除了判断剩余时间,如果当前处理节点已经过期,那么无论现在是否有空闲时间都必须执行该任务。过期时间的大小还代表着任务的优先级

2.协调阶段(reconclie)

将原本react15调和阶段(reconclie)不可停止的大任务按照拆分成了若干小任务,一个任务对应一个节点

两颗fiberTree

react中会存在两个fiberTree:

  • 一个叫做workInprogressTree,指的是当前正在执行更新的fiberTree,在我们初次渲染/执行setState更新状态后,都会构建一个新的fiberTree,我们把它叫做workInprogressTree。
  • 还有一棵fiber树叫做currentFiberTree,表示上次构建好的fiberTree。

在workInprogressTree构建的过程中,会和currentFiberTree进行diff比较(diff在这个阶段发生),把需要变动的fiberNode打上effectTag标记(记录操作的类型),之后收集到effectList中,最终生成一条副作用链effectList(具体作用见下文)

当更新完成以后,使用 workInprogressTree替换掉 currentFiberTree,作为下一次更新的currentFiberTree。

workInprogressTree的构建

workInprogressTree的构建实际上就是循环的执行任务和创建下一个任务

在每次任务执行完毕之后,都会检查还有没有空闲时间,如果没有就把控制权交给浏览器,让浏览器干更重要的事,自己先暂时挂起来,如此循环往复(见下图)

我们常说的diff就发生在workInprogressTree构建的过程之中

通过比较用于构建workInprogressTree中fiber节点的Vnode和currentFiberTree中的fiberNode,来决定如何为workInprogressTree创建fiberNode。

在workInprogressTree构建的过程中,生成fiber节点的方式有三种

  • 克隆(浅拷贝) currentFiber node,意味着原来的 dom 节点可以复用,只需要更新 dom 节点的属性,或者移动 dom 节点;
  • 新建一个 fiberNode,意味着需要新增加一个 dom 节点;
  • 直接复用 currentFiberTree的Node,表示对应的 dom 节点完全不用做任何处理;

3.渲染阶段(commit)

我们执行完协调阶段之后,就该执行渲染阶段了,就需要把变更更新到真实的dom上

那么我们需要再遍历整个fiberTree去检查所有的effectTag吗

不需要,此时我们的effectList就派上用场了,我们在协调阶段所有标记了effectTag的节点都会在effectLIst里面,我们只需要直接遍历它,然后根据对应的effectTag来执行对应的Dom操作就行了。

参考文章:

React Fiber很难?六个问题助你理解 React Fiber

对 React 实现原理的理解

走进React Fiber的世界

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

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

相关文章

百度墨斗鱼文库创作中心源码分析

前言 公司解散,待业中,耗时一天研究了一下百度墨斗鱼文库创作中心源码。实现了后台自动完成任务并通知。 下面主要分析一下实现思路和难点 一,实现思路 调用接口查询未回答的题目列表 合并多个tab下的题目 设置黑白名单,这里…

你知道为什么不用XFP光模块了吗?

在光纤通信应用领域中,10G光模块凭借着较低的成本和功耗被广泛应用于学校、企业等应用场景中。XFP和SFP是10G光模块常见的两种封装类型,那为什么现在市场上XFP光模块应用比较少了呢?下面我们来简单分析一下原因。 一、XFP与SFP光模块的概述 …

从小白到大神之路之学习运维第58天--------Firewalld防火墙

第三阶段基础 时 间:2023年7月12日 参加人:全班人员 内 容: Firewalld防火墙 目录 Firewalld防火墙 一、防火墙 1、netfilter和防火墙管理工具 2、防火墙配置模式 3、Firewalld数据流处理的方式 4、firewalld区域类型 1&#x…

【SVN wc.db 删除不掉的问题】

SVN wc.db 删除不掉的问题 方案1:任务管理器 >性能 >打开资源监视器 > CPU >搜索句柄,关闭相关线程,重试删除。若删除explorer.exe导致资源管理器不显示,在任务管理器新建该任务即可“explorer.exe” 方案2&#xff1…

2023-07-12:RocketMQ如何做到消息不丢失?

2023-07-12:RocketMQ如何做到消息不丢失? 答案2023-07-12: RocketMQ通过刷盘机制、消息拉取机制和ACK机制等多种方式来确保消息投递的可靠性,防止消息丢失。 1.刷盘机制 RocketMQ中的消息分为内存消息和磁盘消息,内…

Acwing.858 Prim算法求最小生成树(朴素Prims算法)

题目 给定一个n个点m条边的无向图,图中可能存在重边和自环,边权可能为负数。求最小生成树的树边权重之和,如果最小生成树不存在则输出impossible。 给定一张边带权的无向图G(V,E),其中V表示图中点的集合,E表示图中边的…

赛效:如何用在线压缩GIF图片

1:在电脑网页上打开并登录快改图,点击左侧菜单栏里的“GIF压缩”。 2:点击页面中间的上传按钮,将电脑本地的GIF文件上传上去。 3:GIF文件上传成功后,设置下方压缩设置,点击右下角“开始压缩”。…

APP外包开发硬件通讯协议

开发APP时会遇到需要与硬件设备通讯的业务场景,常见的硬件设备有健康设备(手环、血压计、血糖仪等)、智能家居设备(冰箱、灯、电视等)、工业设备等等,这些设备的通讯要求各不相同,因此通讯协议也不相同。今天和大家分享这方面的知识&#xff…

buu-Reverse-[2019红帽杯]childRE

目录 [2019红帽杯]childRE 修饰函数名和函数签名是什么? 对于变换部分的具体分析: [2019红帽杯]childRE 下载附件,查壳,无壳 在IDA中打开,定位主函数 int __cdecl main(int argc, const char **argv, const char …

深度学习笔记之Transformer(八)Transformer模型架构基本介绍

机器学习笔记之Transformer——Transformer模型架构基本介绍 引言回顾:简单理解: Seq2seq \text{Seq2seq} Seq2seq模型架构与自编码器自注意力机制 Transformer \text{Transformer} Transformer架构关于架构的简单认识多头注意力机制包含掩码的多头注意力…

kubernetes 1.27.3 集群部署方案

一、准备环境 1.1 Kubernetes 1.27.3 版本集群部署环境准备 1.1.1 主机硬件配置说明 cpu内存硬盘角色主机名系统版本 8C 8G 1024GB master master01 centos 7.9 8C 16G 1024GB worker(node) worker01 centos 7.9 8C 16G 1024GB worker(node) worker…

EasyCVR平台Ehome协议接入,设备管理中出现新增通道按钮的问题优化

EasyCVR可拓展性强、视频能力灵活、部署轻快,可支持的主流标准协议有GB28181、RTSP/Onvif、RTMP等,以及厂家私有协议与SDK接入,包括海康Ehome、海大宇等设备的SDK等,能对外分发RTSP、RTMP、FLV、HLS、WebRTC等格式的视频流。 有用…

软件测试重要性

导言: 在现代软件开发中,软件测试扮演着确保质量的重要角色。通过对软件系统的验证和验证,软件测试有助于发现潜在的缺陷和问题,并确保软件在部署之前能够达到预期的质量标准。本文将深入探讨软件测试的重要性、测试的类型以及成功…

docker常用功能以及mysql实际使用【推荐】

docker常用功能以及mysql实际使用: 一、docker常用命令: 查看版本 docker -v [rootlocalhost ~]# docker -v Docker version 23.0.4, build f480fb1 [rootlocalhost ~]# 2. 查看 Docker 中已存在的镜像 docker images [rootlocalhost ~]# docker ima…

从零开始制作一个Web蜜罐扫描器(3)

从零开始制作一个Web蜜罐扫描器(2)_luozhonghua2000的博客-CSDN博客 那么经过字典的优化,最终就得到了一份ok的字典,如下所示 基本上都是有效的api,那么字典清洗这一步到这里就算完成了。 有了一份好的字典,准确性的问题就迎刃而解。 。 怎么快速且准确且批量的找到蜜储 这…

jenkins实现easyswoole 持续集成/持续部署

jenkins环境jenkins需要使用root用户启动可通过修改 vim /etc/sysconfig/jenkins改为root,也可直接命令行root启动新增流水线项目安装远程构建插件Generic Webhook Trigger勾选触发远程构建保存之后,访问 /generic-webhook-trigger/invoke?tokeneasyswoole-test,即可自动bui…

IDEA中 application.yaml文件没有绿色的叶子

IDEA中 application.yaml文件没有绿色的叶子 问题背景 前段时间一直在刷算法题和备战考试,忽略了项目方面的锻炼,于是今天就想着来写一个练手的项目,重新熟悉一下技术栈。结果刚搭建一个SpringBoot项目,就发现application.yaml配…

支付宝接入

支付宝接入 python-alipay-sdk pycryptodome一、电脑网站支付 1.1 获取支付宝密钥 沙箱网址 1.APPID 2.应用私钥 3.支付宝公钥1.2 存放密钥 在与 settings.py 的同级目录下创建 pem 文件夹pem 文件夹下创建 app_private_key.pem 和 alipay_public_key.pem app_private_key…

分类模型评估指标详解(二分类、多分类、混淆矩阵)

一 、二分类评估 1.混淆矩阵 (ConfusionMatrix) TP:1的预测为1 (正确的积极) 正确判断 FP:0预测为1 (错误的积极) 错误判断 FN:1预测为0 (正确的消极) 漏判断的 TN:0预测为0 (错误的消极) 成功未判断的 准确率:(a…

Unity使用UGUI划线

Unity 里面虽然提供Linerender绘制线条,但是只能在3D空间划线,有时候需要在UI上绘制指定的线条,柱状图,饼状图等就可以采用下面的方式了。 创建DrawLine,继承MaskableGraphic类,重写OnPopulateMesh(VertexH…