React Fiber

React Fiber

~~React 16.8之前的虚拟DOM协调算法,在进行虚拟DOM向真实DOM更新时,会一直占据浏览器资源,导致用户触发的事件无法得到响应,给用户一种卡顿的感觉。React-Fiber可以暂停页面的渲染,让浏览器先执行更高级的任务(比如响应用户触发的事件),等浏览器空闲后再恢复渲染,可以提高浏览器的用户响应速度,并兼顾任务执行效率;延时对DOM的操作,避免一次操作大量DOM~~节点。

对于Fiber的理解,就是ReactReact16.8版本引进了Fiber的概念。相对于以前的版本,Fiber实质上就是在React以前的diff算法中多增加了一个调和阶段。其中requestAnimationFrame是安排优先级高的函数在下一个动画帧之前被调用;requestIdleCallback是安排优先级低的函数在帧结束时的空闲时间被调用。通过使用requestAnimationFrame``和``requestIdleCallback``,React-Fiber可以暂停页面的渲染,让浏览器先执行更高级的任务(比如响应用户触发的事件),等浏览器空闲后再恢复页面渲染,可以提高浏览器的用户响应速度,并兼顾任务执行效率;同时也延时对DOM的操作,避免一次操作大量DOM节点。

Fiber的Scheduler、Reconciler、Renderer

三个Fiber API的作用
  • Scheduler(调度器):排序优先级,让优先级高的任务进行reconcile(调和)。比如:交互事件的优先级最高,要优先把与用户进行交互的事件排到最前面。
  • Reconciler(调和器):找出哪些节点发生了变化,并打标签
  • Renderer(渲染器):将Reconciler中打好标签的节点渲染到页面上(属于commit阶段)
Fiber API的使用

React渲染分为首次渲染和更新渲染,这两个渲染的过程都包括rendercommit两个阶段。

  • 对于首次渲染,React主要的工作就是使用babeljsx进行词法分析和语法分析,然后调用React.createElement方法将jsx对象转化为Fiber树,并根据Fiber树的层级关系,构建生成DOM树并渲染至屏幕中
  • 对于更新渲染时,Fiber树已经存在于内存中了,所以React更关心的是计算出Fiber树中各个节点的差异,并更新到屏幕中。利用Fiber的双缓冲技术,双缓冲共有两颗Fiber树,一棵为current树,展示到页面上的;另一棵是WorkInProgress树,存在于内存中,用来计算变化,然后直接替换current树。先在内存中计算改变,计算完成后一次性更新,这样用户就不会感知到明显的计算变化

两个阶段

  • render阶段:利用Fiber双缓冲技术在内存中构造一棵Fiber树,在其上进行调和计算,找到需要更新的节点并记录,这个过程会被重复中断恢复执行(时间片、主线程让给浏览器执行更高级的任务)
  • commit阶段:根据render阶段的计算结果,执行更新操作,这个过程是同步执行的。

React内部维护了一条执行副作用的单向链表effectList,这些副作用对应的DOM操作在commit阶段执行,除此之外,一些生命周期函数、useEffect等也在commit阶段执行。

commit阶段的工作主要分为三个部分:

  • before mutation阶段(执行DOM操作前):变量赋值、状态重置、调度useEffect
  • mutation阶段(执行DOM操作):创建DOM、遍历effectList
  • layout阶段(执行DOM操作后):可能会触发更新,开启新的render-commit阶段

Fiber的双缓冲技术

Fiber的双缓冲指的是将需要变化的部分,先在内存中计算改变,计算完成后一次性更新,这样用户就不会感知到明显的计算变化。双缓冲共有两颗Fiber树,一棵为current树,展示到页面上的;另一棵是WorkInProgress树,存在于内存中,用来计算变化,然后直接替换current树。双缓冲技术最主要的思想就是先在内存中计算改变,计算完成后一次性更新,这样当用户使用时就不会感觉有卡顿的情况。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0jkBXKj4-1663847199205)(https://secure2.wostatic.cn/static/cZxr8ZyiYw3QVWMwjbo8Kj/v2-f81520d347cc22b36ad5868acb07e6ec_720w.jpg)]

Fiber可中断执行的原理

(Fiber是一个协程,可以随时中断执行)

Fiber的可中断、可继续是基于浏览器的 requestIdleCallback api实现的。目前大多浏览设备每秒刷新60次,也就是16.6ms一次,requestIdleCallback 函数每一帧会返回这一秒渲染完成后剩余的时间,React就会检查现在还剩多少时间,如果没有时间就将控制权交还浏览器;然后继续进行下一帧的渲染。

requestIdleCallback接收两个入参,一个是回调函数,一个是到期时间。这个回调函数的入参也有个入参叫做deadline对象,这个对象的timeRemaining方法会返回当前帧还剩余的时间有多少毫秒。到期时间则是告诉浏览器当这个时间到期时,不管当前帧有没有空闲时间,都必须执行当前帧注册的回调函数。

workLoop就是requestIdleCallback 的回调函数。React 为了在重新渲染界面的时不阻塞浏览器主线程,将渲染任务分为了多个小模块,workLoop就是执行这些小模块任务的函数。workLoop方法首先判断当前帧还有没有时间,如果有空余时间,就继续执行wookloop,如果当前帧没有时间,就让出执行权。浏览器就会在下一帧的空闲时间片内执行工作循环workLoop。这样就实现了浏览器在每一帧的剩余时间片段内都会执行wookLoop函数。这个工作循环会一直执行,即便里面没有任务可执行。这就是Fiber可中断执行的原理。

总结

v16 之前的 React 里,是直接递归遍历 vdom,通过 dom api 增删改 dom 的方式来渲染的。但当 vdom 过大,频繁调用 dom api 会比较耗时,而且递归又不能打断,所以有性能问题。

后来就引入了 fiber 架构,先把 vdom 树转成 fiber 链表,然后再渲染 fiber

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-apdjpa5r-1663847199206)(https://secure2.wostatic.cn/static/pRm9xbAAWiTT3aL4YPwXGn/1661399393348.png)]

vdom fiber 的过程叫做 调和阶段(reconcile),是可打断的,React 加入了任务调度 (schedule) 的机制在空闲时调度 reconcilereconcile 的过程中会做 diff,打上增删改的标记(effectTag),并把对应的 dom 创建好。然后就可以一次性把 fiber 渲染到 dom,也就是 commit

这个 schdulereconcilecommit 的流程就是 fiber 架构。

Fiber可中断执行的原理主要由浏览器的requestIdleCallback 方法实现。requestIdleCallback接收两个参数,一个回调函数workLoop,一个到期时间。React 为了在重新渲染界面的时不阻塞浏览器主线程,将渲染任务分为了多个小模块,workLoop就是执行这些小模块任务的函数。workLoop方法首先判断当前帧还有没有时间,如果有空余时间,就继续执行wookloop,如果当前帧没有时间,就让出执行权。浏览器就会在下一帧的空闲时间片内执行workLoop。这样就实现了浏览器在每一帧的剩余时间片段内都会执行wookLoop函数。这就是Fiber可中断执行的原理。

function lowPriorityWork(deadline) {
  while (deadline.timeRemaining() > 0 && workList.length > 0)
    performUnitOfWork();

  if (workList.length > 0) requestIdleCallback(lowPriorityWork);
}

Fiber 链表

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QgOWecNP-1663847199206)(https://secure2.wostatic.cn/static/pwdqLW4qJYf3yVnhyjHmfP/image.png)]

React 实现了一种树型链表遍历算法。这种算法可以停止遍历并阻止栈增长.不再用递归实现遍历树的。

React 为了在重新渲染界面的时不阻塞浏览器主线程,将渲染任务分为了多个小模块,如果使用树的结构,React只能不断递归,无法将渲染任务转换为一个个增量模块。所以React使用Fiber将树转为树型链表的结构。

如上图所示,每一个element都会对应一个fiber,多个fiber是通过return(指向父)、child(指向子)、sibling(指向兄弟)三个属性建立起联系的。

进程、线程、协程的区别

进程和线程的关系
  1. 进程中任意一线程崩溃都会导致整个进程崩溃
  2. 线程之间可以共享进程中的数据
  3. 当一个进程被关闭后,操作系统会回收进程占用的资源:当一个进程退出时,操作系统会回收该进程所申请的所有资源;即使其中任意线程因为操作不当导致内存泄漏,当进程退出时,这些内存也会被正确回收
  4. 进程之间的内容相互隔离,使OS中的进程互不干扰
进程和线程的区别
  • 进程可以看作独立的应用,线程不能
  • 进程是cpu资源分配的最小单位,是能拥有资源和独立运行的最小单位;线程是cpu调度的最小单位,线程是建立在进程的基础上的一次程序运行单位,一个进程中可以有多个线程。
  • 线程可以共享同一进程中的资源;进程必须通过协助进行通信
  • 进程切换比线程切换开销大。线程切换不会引起进程的切换,但某个进程中的线程切换到另一个进程中的线程时,会引起进程切换
  • 创建和撤销进程时,系统都要为其分配或回收资源
线程和协程的区别
  1. 一个线程可以多个协程,一个进程也可以单独拥有多个协程。
  2. 线程进程都是同步机制,而协程则是异步。
  3. 协程能保留上一次调用时的状态,每次过程重入时,就相当于进入上一次调用的状态。
  4. 线程是抢占式,而协程是非抢占式的,所以需要用户自己释放使用权来切换到其他协程,因此同一时间其实只有一个协程拥有运行权,相当于单线程的能力。协程并不是取代线程, 而且抽象于线程之上, 线程是被分割的CPU资源, 协程是组织好的代码流程, 协程需要线程来承载运行, 线程是协程的资源, 但协程不会直接使用线程, 协程直接利用的是执行器(Interceptor), 执行器可以关联任意线程或线程池, 可以使当前线程, UI线程, 或新建新程.。
  5. 线程是协程的资源。协程通过Interceptor来间接使用线程这个资源。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值