React Fiber 架构理解

2023-12-18 16:38

本文主要是介绍React Fiber 架构理解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

React Fiber 架构理解

引用原文: React Fiber Architecture

React Fiber is an ongoing reimplementation of React's core algorithm. It is the culmination of over two years of research by the React team.

The goal of React Fiber is to increase its suitability for areas like animation, layout,and gestures. Its headline feature is incremental rendering: the ability to split rendering work into chunks and spread it out over multiple frames.

Other key features include the ability to pause, abort, or reuse work as new updates come in; the ability to assign priority to different types of updates; and new concurrency primitives.

React Fibre 是 React 核心算法正在进行的重新实现。它是 React 团队两年多的研究成果。

React Fiber 的目标是提高其对动画,布局和手势等领域的适用性。它的主体特征是增量渲染:能够将渲染工作分割成块,并将其分散到多个帧中。

其他主要功能包括在进行更新时暂停,中止或重新使用工作的能力,为不同类型的更新分配优先权的能力和新的并发原语。

React16之前组件的渲染逻辑

先来看一下react组件渲染时经历的生命周期:

挂载阶段:

  • constructor()
  • componentWillMount()
  • render()
  • componentDidMount()

更新阶段:

  • componentWillReceiveProps()
  • shouldComponentUpdate()
  • componentWillUpdate()
  • render()
  • componentDidUpdate

卸载阶段:

  • componentWillUnmount()

在之前的版本中,如果你实现一个很复杂的深度嵌套的复合组件,会出现下面的情况:

现有层级关系如下的四个组件:

图片描述

组件渲染时调用的生命周期顺序:

图片描述

上图展示的是A,B,C,D的挂载阶段调用的生命周期渲染顺序,可以看到从顶层组件开始调用各生命周期,一直向下,直至调用完最底层子组件的生命周期。然后再向上调用。
组件更新阶段同理。

组件挂载之后,假如修改最上层组件的数据(state),组件更新时的调用栈:

图片描述

如果这是一个很大,层级很深的组件,可以想像到,组件在渲染时,调用栈过长,再加上如果在期间进行了各种复杂的操作,就可能导致长时间阻塞主线程,react渲染它需要几十甚至几百毫秒,这样的话react就会一直占用浏览器主线程,任何其他的操作(包括用户的点击,鼠标移动等操作)都无法执行,带来非常不好的用户体验。

React Fiber的出现

React Fiber 就是为了解决上面的问题而生。

好似一个潜水员,当它一头扎进水里,就要往最底层一直游,直到找到最底层的组件,然后他再上岸。 在这期间,岸上发生的任何事,都不能对他进行干扰,如果有更重要的事情需要他去做(如用户操作),也必须得等他上岸。

Fiber 本质上是一个虚拟的堆栈帧,新的调度器会按照优先级自由调度这些帧,从而将之前的同步渲染改成了异步渲染,在不影响体验的情况下去分段计算更新。它让潜水员会每隔一段时间就上岸,看是否有更重要的事情要做。

对于如何区别优先级,React 有自己的一套逻辑。对于动画这种实时性很高的东西,也就是 16 ms 必须渲染一次保证不卡顿的情况下,React 会每 16 ms(以内) 暂停一下更新,返回来继续渲染动画。

React Fiber 架构

调度拆分为小任务

浏览器本身也不断进化中,随着页面由简单的展示转向WebAPP,它需要一些新能力来承载更多节点的展示与更新。
下面是一些自救措施:

  • requestAnimationFrame
  • requestIdleCallback
  • web worker
  • IntersectionObserver

react官方采用的是 requestIdleCallback,为了兼容所有平台,facebook 单独实现了其功能,作为一个独立的 npm 包使用 react-schedule

其作用是会在浏览器空闲时期依次调用函数, 这就可以在主事件循环中执行后台或低优先级的任务,而且不会对像动画和用户交互这样延迟触发而且关键的事件产生影响。函数一般会按先进先调用的顺序执行,除非函数在浏览器调用它之前就到了它的超时时间。

简化后的大致流程图如下:

图片描述

Fiber Node 及 Fiber Tree

  • 从流程图上看到会有 Fiber Node 节点,这个是在 react 生成的 Virtual Dom 基础上增加的一层数据结构,主要是为了将递归遍历转变成循环遍历,配合 requestIdleCallback API, 实现任务拆分、中断与恢复。为了实现循环遍历,Fiber Node 上携带了更多的信息。
  • 每一个 Fiber Node 节点与 Virtual Dom 一一对应,所有 Fiber Node 连接起来形成 Fiber tree, 是个单链表树结构

两个阶段:reconciliation 和 commit

对于异步渲染,现在渲染有两个阶段:reconciliation 和 commit 。前者过程是可以打断的,后者不能暂停,会一直更新界面直到完成。

reconciliation 处理过程

  • 当执行 setState() 或首次 render() 时,进入工作循环,循环体中处理的单元为 Fiber Node, 即是拆分任务的最小单位,从根节点开始,自顶向下逐节点构造 workInProgress tree(构建中的新 Fiber Tree)。
  • 每个工作处理单元做的事情,由 beginWork(), completeUnitOfWork() 两部分构成。
  • beginWork()主要做的事情是从顶向下生成所有的 Fiber Node,并标记 Diff, 不包括兄弟节点,每个 Fiber Node 的处理过程根据组件类型略有差异,以 ClassComponent 为例:

1 如果当前节点不需要更新,直接把子节点clone过来,要更新的话标记更新类型
2 更新当前节点状态(props, state, context等)
3 调用shouldComponentUpdate()
4 调用组件实例方法 render() 获得新的子节点,并为子节点创建 Fiber Node(创建过程会尽量复用现有 Fiber Node,子节点增删也发生在这里)
5 如果没有产生 child fiber,进入下一阶段 completeUnitOfWork

  • completeUnitOfWork() 当没有子节点,开始遍历兄弟节点作为下一个处理单元,处理完兄弟节点开始向上回溯,直到再次回去根节点为止,将收集向上回溯过程中的所有 diff,拿到 diff 后开始进入 commit 阶段。
  • 构建 workInProgress tree 的过程就是 diff 的过程,通过 requestIdleCallback 来调度执行一组任务,每完成一个任务后回来看看有没有插队的(更紧急的),把时间控制权交还给主线程,直到下一次 requestIdleCallback 回调再继续构建workInProgress tree。

两个阶段涉及到的生命周期:

Reconciliation 阶段 (React算法,用来比较2颗树,以确定哪些部分需要重新渲染

  • componentWillMount
  • componentWillReceiveProps
  • shouldComponentUpdate
  • componentWillUpdate

Commit 阶段 (用于呈现React应用的数据更改。通常是setState的结果。最终导致重新渲染。

  • componentDidMount
  • componentDidUpdate
  • componentWillUnmount

因为 reconciliation 阶段是可以被打断的,所以 reconciliation 阶段会执行的生命周期函数就可能会出现调用多次的情况,从而引起 Bug。所以对于 reconciliation 阶段调用的几个函数,除了 shouldComponentUpdate 以外,其他都应该避免去使用,并且 React16 中也引入了新的 API 来解决这个问题。

于是官方推出了getDerivedStateFromProps,让你在render设置新state,你主要返回一个新对象,它就主动帮你setState。由于这是一个静态方法,你不能取到 this,当然你也不能操作instance,这就阻止了你多次操作setState。这样一来,getDerivedStateFromProps的逻辑应该会很简单,这样就不会出错,不会出错,就不会打断DFS过程。

getDerivedStateFromProps取代了原来的componentWillMountcomponentWillReceiveProps方法,该函数会在组件 初始化 和 更新 时被调用

class ExampleComponent extends React.Component {// Initialize state in constructor,// Or with a property initializer.state = {};static getDerivedStateFromProps(nextProps, prevState) {if (prevState.someMirroredValue !== nextProps.someValue) {return {derivedData: computeDerivedState(nextProps),someMirroredValue: nextProps.someValue};}// Return null to indicate no change to state.return null;}
}

在进入commi阶段时,组件多了一个新钩子叫getSnapshotBeforeUpdate,它与commit阶段的钩子一样只执行一次。
getSnapshotBeforeUpdate 用于替换 componentWillUpdate ,该函数会在 update 后 DOM 更新前被调用,用于读取最新的 DOM 数据。

于是整个流程变成这样:(引用大神@司徒正美的图)

React16 生命周期函数用法建议

结合 React Fiber 架构 建议如下使用react生命周期

class ExampleComponent extends React.Component {// 用于初始化 stateconstructor() {}// 用于替换 `componentWillReceiveProps` ,该函数会在初始化和 `update` 时被调用// 因为该函数是静态函数,所以取不到 `this`// 如果需要对比 `prevProps` 需要单独在 `state` 中维护static getDerivedStateFromProps(nextProps, prevState) {}// 判断是否需要更新组件,多用于组件性能优化shouldComponentUpdate(nextProps, nextState) {}// 组件挂载后调用// 可以在该函数中进行请求或者订阅componentDidMount() {}// 用于获得最新的 DOM 数据getSnapshotBeforeUpdate() {}// 组件即将销毁// 可以在此处移除订阅,定时器等等componentWillUnmount() {}// 组件销毁后调用componentDidUnMount() {}// 组件更新后调用componentDidUpdate() {}// 渲染组件函数render() {}// 以下函数不建议使用UNSAFE_componentWillMount() {}UNSAFE_componentWillUpdate(nextProps, nextState) {}UNSAFE_componentWillReceiveProps(nextProps) {}
}

16 大版本主要更新还解决以下痛点:

  • 组件不能返回数组,最见的场合是UL元素下只能使用LI,TR元素下只能使用TD或TH,这时这里有一个组件循环生成LI或TD列表时,我们并不想再放一个DIV,这会破坏HTML的语义。
  • 弹窗问题,之前一直使用不稳定的unstable_renderSubtreeIntoContainer。弹窗是依赖原来DOM树的上下文,因此这个API第一个参数是组件实例,通过它得到对应虚拟DOM,然后一级级往上找,得到上下文。它的其他参数也很好用,但这个方法一直没有转正。。。
  • 异常处理,我们想知道哪个组件出错,虽然有了React DevTool,但是太深的组件树查找起来还是很吃力。希望有个方法告诉我出错位置,并且出错时能让我有机会进行一些修复工作
  • HOC的流行带来两个问题,毕竟是社区兴起的方案,没有考虑到ref与context的向下传递。
  • 组件的性能优化全凭人肉,并且主要集中在SCU,希望框架能干些事情,即使不用SCU,性能也能上去。

新特性:

  • render / 纯组件能够 return 任何数据结构
  • CreatePortal API,更好的处理 Dialog 这种场景组件
  • 新的 context api,尝试代替一部分 redux 的职责
  • 异步渲染/时间切片(time slicing),成倍提高性能
  • componentDidCatch,错误边界,框架层面上提高用户 debug 的能力
  • 网络请求 IO(Suspense),更好的处理异步网络 IO

参考资料:

  • React Fiber架构
  • React Fiber Architecture
  • React系列——React Fiber 架构介绍资料汇总(翻译+中文资料)
  • InterviewMap——React周期分析

原文https://segmentfault.com/a/1190000018701625?utm_source=tuicool&utm_medium=referral

这篇关于React Fiber 架构理解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/509104

相关文章

Vue3 的 shallowRef 和 shallowReactive:优化性能

大家对 Vue3 的 ref 和 reactive 都很熟悉,那么对 shallowRef 和 shallowReactive 是否了解呢? 在编程和数据结构中,“shallow”(浅层)通常指对数据结构的最外层进行操作,而不递归地处理其内部或嵌套的数据。这种处理方式关注的是数据结构的第一层属性或元素,而忽略更深层次的嵌套内容。 1. 浅层与深层的对比 1.1 浅层(Shallow) 定义

mybatis的整体架构

mybatis的整体架构分为三层: 1.基础支持层 该层包括:数据源模块、事务管理模块、缓存模块、Binding模块、反射模块、类型转换模块、日志模块、资源加载模块、解析器模块 2.核心处理层 该层包括:配置解析、参数映射、SQL解析、SQL执行、结果集映射、插件 3.接口层 该层包括:SqlSession 基础支持层 该层保护mybatis的基础模块,它们为核心处理层提供了良好的支撑。

百度/小米/滴滴/京东,中台架构比较

小米中台建设实践 01 小米的三大中台建设:业务+数据+技术 业务中台--从业务说起 在中台建设中,需要规范化的服务接口、一致整合化的数据、容器化的技术组件以及弹性的基础设施。并结合业务情况,判定是否真的需要中台。 小米参考了业界优秀的案例包括移动中台、数据中台、业务中台、技术中台等,再结合其业务发展历程及业务现状,整理了中台架构的核心方法论,一是企业如何共享服务,二是如何为业务提供便利。

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

【 html+css 绚丽Loading 】000046 三才归元阵

前言:哈喽,大家好,今天给大家分享html+css 绚丽Loading!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏+关注哦 💕 目录 📚一、效果📚二、信息💡1.简介:💡2.外观描述:💡3.使用方式:💡4.战斗方式:💡5.提升:💡6.传说: 📚三、源代码,上代码,可以直接复制使用🎥效果🗂️目录✍️

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

认识、理解、分类——acm之搜索

普通搜索方法有两种:1、广度优先搜索;2、深度优先搜索; 更多搜索方法: 3、双向广度优先搜索; 4、启发式搜索(包括A*算法等); 搜索通常会用到的知识点:状态压缩(位压缩,利用hash思想压缩)。

【生成模型系列(初级)】嵌入(Embedding)方程——自然语言处理的数学灵魂【通俗理解】

【通俗理解】嵌入(Embedding)方程——自然语言处理的数学灵魂 关键词提炼 #嵌入方程 #自然语言处理 #词向量 #机器学习 #神经网络 #向量空间模型 #Siri #Google翻译 #AlexNet 第一节:嵌入方程的类比与核心概念【尽可能通俗】 嵌入方程可以被看作是自然语言处理中的“翻译机”,它将文本中的单词或短语转换成计算机能够理解的数学形式,即向量。 正如翻译机将一种语言

计算机毕业设计 大学志愿填报系统 Java+SpringBoot+Vue 前后端分离 文档报告 代码讲解 安装调试

🍊作者:计算机编程-吉哥 🍊简介:专业从事JavaWeb程序开发,微信小程序开发,定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事,生活就是快乐的。 🍊心愿:点赞 👍 收藏 ⭐评论 📝 🍅 文末获取源码联系 👇🏻 精彩专栏推荐订阅 👇🏻 不然下次找不到哟~Java毕业设计项目~热门选题推荐《1000套》 目录 1.技术选型 2.开发工具 3.功能

【C++高阶】C++类型转换全攻略:深入理解并高效应用

📝个人主页🌹:Eternity._ ⏩收录专栏⏪:C++ “ 登神长阶 ” 🤡往期回顾🤡:C++ 智能指针 🌹🌹期待您的关注 🌹🌹 ❀C++的类型转换 📒1. C语言中的类型转换📚2. C++强制类型转换⛰️static_cast🌞reinterpret_cast⭐const_cast🍁dynamic_cast 📜3. C++强制类型转换的原因📝