理解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算法的复杂度。


目录
相关文章
|
4天前
|
前端开发 NoSQL 数据库
切图仔做全栈:React&Nest.js社区平台(一)——基础架构与邮箱注册、JWT登录实现
切图仔做全栈:React&Nest.js社区平台(一)——基础架构与邮箱注册、JWT登录实现
|
5月前
|
移动开发 前端开发 JavaScript
react fiber架构【详细讲解,看这一篇就够了】
react fiber架构【详细讲解,看这一篇就够了】
170 0
|
9月前
|
前端开发 JavaScript 算法
【说说你对fiber架构的理解?解决了什么问题?】
【说说你对fiber架构的理解?解决了什么问题?】
|
9月前
|
前端开发 JavaScript 算法
对Fiber架构的理解?解决了什么问题?
对Fiber架构的理解?解决了什么问题?
69 0
|
9月前
|
前端开发
前端学习笔记202307学习笔记第五十七天-react源码-Fiber数据结构介绍2
前端学习笔记202307学习笔记第五十七天-react源码-Fiber数据结构介绍2
37 0
|
9月前
|
前端开发
前端学习笔记202307学习笔记第五十七天-react源码-react16使用得架构1
前端学习笔记202307学习笔记第五十七天-react源码-react16使用得架构1
31 0
|
9月前
|
前端开发
前端学习笔记202307学习笔记第五十七天-react源码-Fiber数据结构介绍1
前端学习笔记202307学习笔记第五十七天-react源码-Fiber数据结构介绍1
45 0
|
移动开发 JavaScript Java
|
4月前
|
设计模式 前端开发 数据可视化
【第4期】一文了解React UI 组件库
【第4期】一文了解React UI 组件库
107 0
|
4月前
|
资源调度 前端开发 JavaScript
React 的antd-mobile 组件库,嵌套路由
React 的antd-mobile 组件库,嵌套路由
42 0