理解React中Fiber架构(一)

简介: 自从React16版本更新了Hook用法,同时引入了新的Fiber架构去重构整个渲染和事件处理过程,React团队引入Hook是为了更好剥离业务代码,让开发能更加友好的抽象代码,达到低耦合的函数组件目的,那么重构Diff算法,引入Fiber架构是为了什么呢? 其实只是为了能够一个目标快速响应,原先Diff算法时间复杂度为O(n3) O(n^3)O(n3) ,最后经过Fiber重构达到了O(n)O(n)O(n),这里面具体有什么门道,值得我们去深入研究一下。

背景


自从React16版本更新了Hook用法,同时引入了新的Fiber架构去重构整个渲染和事件处理过程,React团队引入Hook是为了更好剥离业务代码,让开发能更加友好的抽象代码,达到低耦合的函数组件目的,那么重构Diff算法,引入Fiber架构是为了什么呢? 其实只是为了能够一个目标快速响应,原先Diff算法时间复杂度为O(n3) O(n^3)O(n3) ,最后经过Fiber重构达到了O(n)O(n)O(n),这里面具体有什么门道,值得我们去深入研究一下。

问题


在了解Fiber架构之前,我们需要对原有React16之前版本是有什么问题,才需要引入Fiber架构去解决该问题?


React15及以前的版本采用的是Stack Reconciler(栈协调器)架构,使用同步递归方式去创建虚拟DOM,一旦进入创建过程,就无法中断,如果创建过程超过16ms,用户就会出现页面卡顿感觉。具体可以参考下图:

80063dcbac35d25bfc0a1e44713f325.png

因此,从网上搜索了一下React15及以前的版本反馈,的主要问题有如下几个:

  • React的动画效果表现不佳
  • React在有大量DOM节点渲染卡顿

为什么


为什么会出现卡顿的情况,主要原因如下:

1.JavaScript是单线程,与渲染线程互斥,当其中一个线程执行时,另一个线程只能挂起等待。

2.Stack Reconciler 栈协调器某个任务是长期占用JavaScript主线程

前置知识


为了更好了解Fiber架构设计,需要提前了解一些前置知识,每个知识点其实都需要深入了解,这里只是简单描述,主要有以下几点:

  • 单线程的 JavaScript 与多线程的浏览器
  • React生命周期
  • React虚拟DOM

单线程的 JavaScript 与多线程的浏览器


在我们学习前端知识的时候,有个结论是: 单线程的 JavaScript 与多线程的浏览器


一个完整的web网页在浏览器显示和交互的进程(chrome为主),需要涉及到线程主要以下几个部分:

  • GUI 渲染线程,负责渲染浏览器界面HTML元素,当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行。
  • JavaScript引擎线程,JS内核,负责处理Javascript脚本程序。 一直等待着任务队列中任务的到来,然后解析Javascript脚本,运行代码。
  • 定时触发器线程,定时器setInterval与setTimeout所在线程,为什么要单独弄个线程处理定时器?是因为JavaScript引擎是单线程的, 如果处于阻塞线程状态就会影响记计时的准确
  • 事件触发线程,用来控制事件轮询,JS引擎自己忙不过来,需要浏览器另开线程协助
  • 异步http请求线程,在XMLHttpRequestfetch在连接后是通过浏览器新开一个线程请求, 将检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件放到 JavaScript引擎的处理队列中等待处理。这里需要注意XMLHttpRequestfetch的区别,fetch是w3c标准化后一个专门提供给开发调用发起http的API接口,XMLHttpRequest是一个非标准化的Http请求对象,主要是可以发起http请求获取XML数据。


上述就是浏览器的多线程,然后单线程的JavaScript通常指的是JavaScript引擎线程,为什么需要单线程?因为多线程可能会出现各种UI交互冲突问题。因此了解单线程JS需要注意几点:

  • GUI线程和JS引擎是互斥的,当JS引擎执行时GUI线程会被挂起,GUI更新则会被保存在一个队列中等到JS引擎线程空闲时立即被执行。
  • JS 引擎只是任意的 JS 代码按需执行的环境,是其他线程调用触发JS引擎执行JS代码,比如:一个按钮点击触发事件,接着调用js引擎执行等


JS 引擎工作流程图如下:

c1fda3bc4d9d22f03ee0219fb711b85.png

React 生命周期


为了更好了解React Fiber架构,我们需要对比React15和React16的生命周期,具体如下:

React15的生命周期


在15版本的时候,一个完整的组件生命周期如下(按照执行顺序):

  • constructor(),组件的构造函数,用来初始化state
  • componentWillMount(),初始化渲染前时调用
  • componentDidMount(),初始化渲染后调用
  • componentWillReceiveProps(),父组件修改组件的props时会调用
  • render(),每次渲染时候会调用
  • componentWillUpdate(),组件更新前调用
  • shouldComponentUpdate(),组件更新时调用,主要判断组件要不要更新
  • componentDidUpdate(),组件更新后调用
  • componentWillUnmount(),组件卸载时调用


按照不同时期,执行过程是不一样,具体可以见React的生命周期更改相关文章。

React16生命周期


相比较React15,16版本基于Fiber架构主要对更新周期的函数做了调整,整个生命周期如下:

  • constructor(),组件的构造函数,用来初始化state
  • getDerivedStateFromProps(),初始化/更新时调用,使用 props 来派生/更新 state。
  • componentDidMount(),初始化渲染后调用
  • shouldComponentUpdate(),
  • render(),每次渲染时候会调用
  • shouldComponentUpdate(),组件更新时调用,主要判断组件要不要更新
  • getSnapshotBeforeUpdate(),返回值会作为第三个参数给到 componentDidUpdate。它的执行时机是在 render 方法之后,真实 DOM 更新之前。可以同时获取到更新前的真实 DOM 和更新前后的 state&props 信息。
  • componentDidUpdate(),组件更新后调用,从 getSnapshotBeforeUpdate 获取到的值
  • componentWillUnmount(),组件卸载时调用


对比一下,React 16 废弃的是哪些生命周期:

  • componentWillMount;
  • componentWillUpdate;
  • componentWillReceiveProps


这些生命周期的共性,就是它们都处于 render 阶段,都可能重复被执行,而且由于这些 API 常年被滥用,它们在重复执行的过程中都存在着不可小觑的风险。


为什么废弃这些生命周期,因为引用了Fiber架构,render 阶段是允许暂停、终止和重启的。这就导致 render 阶段的生命周期都是有可能被重复执行的。

React 虚拟DOM


虚拟 DOM(Virtual DOM)本质上是JS 和 DOM 之间的一个映射缓存,它在形态上表现为一个能够描述 DOM 结构及其属性信息的 JS 对象。

记住两个点:

  • 虚拟 DOM 是 JS 对象
  • 虚拟 DOM 是对真实 DOM 的描述

虚拟DOM出现react生命周期的两个节点:

1.挂载阶段,React 将结合 JSX 的描述,构建出虚拟 DOM 树,然后通过 ReactDOM.render 实现虚拟 DOM 到真实 DOM 的映射

2.更新阶段,页面的变化在作用于真实 DOM 之前,会先作用于虚拟 DOM,虚拟 DOM 将在 JS 层借助算法先对比出具体有哪些真实 DOM 需要被改变,然后再将这些改变作用于真实 DOM,这里就需要DOM Diff算法。


为什么需要虚拟DOM?并不是因为虚拟DOM有更高的性能,而是因为虚拟 DOM 的优越之处在于,它能够在提供更爽、更高效的研发模式(也就是函数式的 UI 编程方式)的同时,仍然保持一个还不错的性能。解决了以下问题:

1.研发体验/研发效率的问题,解决以往模板和数据,需要重复调整的问题

2.跨平台的问题,从web、小程序、app等,一套虚拟DOM,结合不同渲染逻辑,满足各类跨端场景


而在虚拟DOM这一块,Fiber架构的引入,最大的调整就是虚拟DOM更新中的diff算法,由于分片渲染,不需要一次将diff执行,可以分批计算从而减少diff算法的复杂度。


目录
相关文章
|
10天前
|
前端开发 调度 UED
React 执行过程中 Fiber 的优先级是如何确定的?
【10月更文挑战第27天】React能够更加智能地管理任务的执行顺序,在保证用户交互体验的同时,充分利用系统资源,提高应用的整体性能和响应速度。
|
1月前
|
存储 前端开发 JavaScript
深入理解React Fiber架构及其性能优化
【10月更文挑战第5天】深入理解React Fiber架构及其性能优化
74 1
|
10天前
|
前端开发 JavaScript 调度
React 的 fiber
【10月更文挑战第26天】React Fiber 是 React 框架中的一个重要创新,它为 React 应用的性能优化和用户体验提升提供了强大的支持,使得 React 在处理复杂的前端应用场景时更加高效和灵活。
|
3月前
|
缓存 前端开发 算法
Fiber 架构如何提高性能和响应性的
【8月更文挑战第6天】Fiber 架构如何提高性能和响应性的
48 1
|
3月前
|
缓存 前端开发 JavaScript
理解 React 的 Fiber 架构
【8月更文挑战第6天】 理解 React 的 Fiber 架构
164 1
|
6月前
|
前端开发 NoSQL 数据库
切图仔做全栈:React&Nest.js社区平台(一)——基础架构与邮箱注册、JWT登录实现
切图仔做全栈:React&Nest.js社区平台(一)——基础架构与邮箱注册、JWT登录实现
|
9天前
|
弹性计算 Kubernetes Cloud Native
云原生架构下的微服务设计原则与实践####
本文深入探讨了在云原生环境中,微服务架构的设计原则、关键技术及实践案例。通过剖析传统单体架构面临的挑战,引出微服务作为解决方案的优势,并详细阐述了微服务设计的几大核心原则:单一职责、独立部署、弹性伸缩和服务自治。文章还介绍了容器化技术、Kubernetes等云原生工具如何助力微服务的高效实施,并通过一个实际项目案例,展示了从服务拆分到持续集成/持续部署(CI/CD)流程的完整实现路径,为读者提供了宝贵的实践经验和启发。 ####
|
1天前
|
存储 监控 Docker
探索微服务架构下的容器化部署
本文旨在深入探讨微服务架构下容器化部署的关键技术与实践,通过分析Docker容器技术如何促进微服务的灵活部署和高效管理,揭示其在现代软件开发中的重要性。文章将重点讨论容器化技术的优势、面临的挑战以及最佳实践策略,为读者提供一套完整的理论与实践相结合的指导方案。
|
6天前
|
监控 安全 应用服务中间件
微服务架构下的API网关设计策略与实践####
本文深入探讨了在微服务架构下,API网关作为系统统一入口点的设计策略、实现细节及其在实际应用中的最佳实践。不同于传统的摘要概述,本部分将直接以一段精简的代码示例作为引子,展示一个基于NGINX的简单API网关配置片段,随后引出文章的核心内容,旨在通过具体实例激发读者兴趣,快速理解API网关在微服务架构中的关键作用及实现方式。 ```nginx server { listen 80; server_name api.example.com; location / { proxy_pass http://backend_service:5000;
|
8天前
|
缓存 监控 API
探索微服务架构中的API网关模式
随着微服务架构的兴起,API网关成为管理和服务间交互的关键组件。本文通过在线零售公司的案例,探讨了API网关在路由管理、认证授权、限流缓存、日志监控和协议转换等方面的优势,并详细介绍了使用Kong实现API网关的具体步骤。
25 3