zoukankan      html  css  js  c++  java
  • React Fiber

    在了解一个知识架构的时候,为了能更彻底的了解这个知识架构,我们通常会围绕着以下几个问题展开来理解。

    • 什么是React Fiber
    • React Fiber产生的原因(React Fiber解决了什么问题)
    • 详细了解React Fiber

    1.什么是fiber

    首先fiber是操作系统的概念,即纤程。是更轻量级的线程,一个线程可以包含多个纤程。React在这里利用这个特点(注意是特点,意图相同)来命名React Fiber。

    2.React Fiber产生的原因

    Fiber诞生之前,React运行机制是采用一把梭的方式进行虚拟dom对比以及更新视图,大致过程如下

    第一步:首先创建一颗虚拟dom tree

    第二步:在状态改变的时候生成一颗新的虚拟dom tree

    第三步:两个树进行对比得到更新的节点

    第四步:将得到的更新节点对应生成真实dom

    这种一把梭的流程弊端就是js执行时间过长导致UI卡顿甚至掉帧。至于为何UI卡顿或者掉帧呢~到这里就需要考虑一些关于浏览器原理问题。

    浏览器原理

    浏览器是多线程运行,包含JavaScript线程,事件线程,定时任务线程,HTTP请求线程,UI渲染线程等等,但是这里JS线程与UI线程是互斥的,

    也就说是这俩只能执行其中一个线程,每当一个线程在执行的时候,另一个线程都会被挂起,原因很简单,JS线程可以操作dom,如果同时执行

    会带来很多不可控问题。当JS线程执行时间过长的时候就会导致UI渲染线程一直被挂起,可操作时间就会一直推迟,导致的效果就是页面不响应,

    卡顿,甚至掉帧的可能。

    React Fiber出来之前,传统模式的React再进行协调(这里我只考虑这个阶段,因为映射真实dom阶段不会有太大的消耗)会有比较大的性能开销,

    当然React在这个阶段已经做了这个优化,比如优化虚拟dom tree对比的过程。但在复杂场景仍然会导致这个问题。好了既然说到JS执行时间过长,那么

    多少时间算合理呢~

    通常设备的帧率是60次/秒,也就是一帧需要控制在16.67ms之内才能保证一个非常理想的交互体验,浏览器的一帧组成如同

    从浏览器一帧的结构中可以观察到,渲染之间还有events,javascript,begin frame,rAF要处理,一帧中,需要将JS执行时间控制到一定范围,

    才不会影响渲染。所以React Fiber主要的任务就是将传统React协调方式进行优化,从而达到每次运行可以控制在一个合理的范围,达到一个理

    想的UI交互,这就是React Fiber要解决的问题。

    ps:这里不考虑UI渲染部分的耗时。

    3.详细了解React Fiber

     React Fiber

    (1)React Fiber 在reconciliation阶段的协调核心算法重新实现

    (2)链表结构的Fiber tree,方便在reconciliation阶段做中断以及回溯

    (3)可控调用栈

    React Fiber重新实现

    首先了解一帧的执行过程

    在一帧中执行可以通过插入requestIdleCallback来执行定向操作,但是requestIdleCallback调用是在渲染之后,这就导致了,我们不可以在这里

    再进行dom操作,再次操作dom会导致重新渲染,导致结果不可控。

    来自MDN的解释:

    window.requestIdleCallback()方法插入一个函数,这个函数将在浏览器空闲时期被调用。这使开发者能够在主事件循环上执行后台和低优先级工作,而不会影响延迟关键事件,如动画和输入响应。函数一般会按先进先调用的顺序执行,然而,如果回调函数指定了执行超时时间timeout,则有可能为了在超时前执行函数而打乱执行顺序。(来自:https://developer.mozilla.org/zh-CN/docs/Web/API/Window/requestIdleCallback)

    React内部则是避开了requestIdleCallback采用window.requestAnimationFrame来动态调整帧率,计算JS剩余时间是否超过React设定的阈值16ms,从而实现

    时间分片,原因就是requestAnmiationFrame再渲染之前执行,在这里做任何操作都不会影响到渲染的结果。React Fiber 规定一帧内JS执行的最大时间,即阈值16ms

    来自MDN的解释:

    window.requestAnimationFrame() 告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行

    注意:若你想在浏览器下次重绘之前继续更新下一帧动画,那么回调函数自身必须再次调用window.requestAnimationFrame()

    JS在执行过程,一个工作单元由JS引擎启动,一把梭得形式执行,只要开始就无法中断,直到整个工作单元执行结束。如图

    React Fiber得目的是将一个繁重得任务进行拆分,拆分成一个个小的单元来进行执行,(这里就是fiber纤程得概念)按照优先级高低得方式

    进行执行这一个个小的单元任务,这就是时间分片得概念。如下图

    总结Fiber 大致调度过程

    (1)state改变执行一个同步任务,将其拆分成一个任务队列

    (2)在任务队列中按照任务优先级执行,如果执行超过了deadline,这状态设置为pending状态挂起

    (3)一个fiber任务的执行或者挂起,根据requestIdleRequest/requestAnimationRequest实现的调度器,返一个新的fiber任务队列继续执行。

     任务优先级

    sync

    InteractiveExpiration 

    AsyncExpiration

    开发者视角

    unstable_scheduleCallback(() => {
      // 异步低优先级任务,AsyncExpiration
      this.setState();
    });
    flushSync(() => {
      // 同步任务,最高优先级
      this.setState();
    });
    onClick={() => {
      // 异步高优先级任务,InteractiveExpiration
      this.setState();
    }}

    Fiber节点

    一个fiber结构是一个js对象,对应通过React.createElement来创建一个虚拟dom节点,整个虚拟dom树上得所有节点都对应一个fiber节点,这个fiber节点上

    除了包含元素信息意外,还包含了一些其他信息,用来支撑整个调度算法。

    fiber节点上重要得信息就是包含了三个“指针”,分别是child,sibling,return,用来遍历整个fiber树。

    child指向第一个儿子节点(其余儿子节点都是基于第一个儿子节点通过sibling查找)

    sibling指向兄弟节点

    return指向父级节点

    Fiber 树的结构图

    得益于fiber节点指针以及整颗树得链表结构,可以实现任务分片,任务中断,按照优先级调度。

    整个fiber树执行过程

    render阶段

    1创建当前current树

    2状态改变生成workInProgress树

    3协调生成effecList链表(变更得节点链表)

    commit阶段

    4将effectList链表映射到真实dom

    Update更新过程

    (1)记录节点状态

    (2)将变更节点放到updateQueue中

    (3)统一更新到UI视图层

    setState干了什么

    setState本质上只是一个改变state得命令,可以理解为是一个视图更新得请求,但是试图更新不是setState干得事儿。当执行了setState得时候

    改变了state,得到变更得fiber节点,然后将变更得节点push到了updateQueue中,在React会统一在一个时间点处理整个updateQueue内得节点

    我们可以观察到一个很常见得事儿,就是在调用setState方法之后state并没有及时更新。上述就是原因,举个例子

    当在一个合成事件内同时执行了多个setState方法,比如针对一个count,多次进行了+1操作,得到得结果state只是1。原因就是,多次执行的时候

    第一次执行setState的时候count进行+1的操作,但是state并没有及时改变,再次执行的时候state还是原来的值,这样多次操作其实效果就是一次操作

    的效果,只是连续往updateQueue中push了count+1的第一次得到的值。

  • 相关阅读:
    Android Studio代码自己主动检測错误提示
    uva 1567
    UWP 新手教程2——怎样实现自适应用户界面
    远程服务的使用场景
    本地服务和远程服务
    本地应用调用远程服务中的方法
    混合方式开启服务
    绑定服务抽取接口
    绑定服务调用服务里的方法
    bind绑定服务的生命周期
  • 原文地址:https://www.cnblogs.com/moran1992/p/15761059.html
Copyright © 2011-2022 走看看