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

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

目录

渲染过程

react15

react16

为什么要引入fiber

不可中断原因

为什么vue不需要fiber架构

fiber详解

是什么

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

结构

具体过程

1.调度阶段(schedule)

2.协调阶段(reconclie)

两颗fiberTree

workInprogressTree的构建

3.渲染阶段(commit)


渲染过程

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

当调用render(初次渲染页面)或者setXXX方法(数据更新导致页面重新渲染)时候,会引发页面的渲染,下面主要讲述的是使用setXXX的情况。

react15

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

1.协调/调和阶段(reconclie):在这个阶段 React 会更新数据生成新的 虚拟Dom树,然后通过Diff算法,从该节点/组件(使用useState声明该state的节点,而非调用setXXX的节点)开始往下递归(该节点&所有子节点虚拟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)的信息,如果我们打断了,是找不到它的父亲和兄弟节点的,所以就又得重头开始找。

为什么vue不需要fiber架构

该问题可见笔者另一篇文章,写的很详细:为什么vue不需要fiber架构?_Bule Guy的博客-CSDN博客

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的世界

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1. 为了搭建一个React项目,首先要确保你的开发环境已经配置好了。你需要安装Node.js和npm(Node包管理器)。 2. 使用命令行工具进入你想要保存项目的文件夹,并执行以下命令来创建一个新的React应用: ```shell npx create-react-app my-app ``` 这将使用create-react-app脚手架工具创建一个新的React应用,并在my-app文件夹中初始化项目结构。 3. 进入my-app文件夹,并启动开发服务器: ```shell cd my-app npm start ``` 这将启动一个本地开发服务器,并在浏览器中打开http://localhost:3000以查看你的React应用。 4. 现在你可以开始编写你的React组件了。你可以创建函数组件或类组件来定义你的UI。函数组件是一个纯函数,接收props对象并返回一个React元素;而类组件需要继承React.Component,并实现一个render函数来返回React元素。 5. 如果你需要在组件之间进行通信,可以使用Redux这样的状态管理库。Redux可以帮助你管理全局的状态,并使不同组件之间共享数据变得更加容易。你可以使用npm来安装Redux和相关的库: ```shell npm install redux react-redux ``` 然后,你可以在你的应用中创建Redux的store,并通过Provider组件将它提供给整个应用。你可以使用connect函数来连接你的组件,使其能够访问Redux中的状态。 6. 最后,你可以使用第三方UI库来加快你的开发速度。Ant Design是一个流行的React UI框架,它提供了丰富的组件和样式,可以帮助你快速构建漂亮的用户界面。你可以使用npm来安装Ant Design: ```shell npm install antd ``` 然后,你可以在你的组件中引入Ant Design的组件,并使用它们来构建你的UI。Ant Design的文档提供了详细的使用说明和示例代码,可以帮助你更好地了解和使用该框架。 以上是从0到1搭建一个React项目的一般步骤。你可以根据实际需求和项目要求进行相应的调整和扩展。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [REACT实战项目从0到1搭建(仅供参考)](https://blog.csdn.net/qq_31851435/article/details/121740400)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [react-从0到1新建react项目](https://blog.csdn.net/weixin_40220492/article/details/127145343)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值