react fiber摘要

react理念


前言

react fiber是react 16 的一种架构模型,下文会介绍react的理念,以及fiber产生的原由,fiber的实现原理和工作原理


提示:以下是本篇文章正文内容,下面案例可供参考

一、react理念

1.问题阐述

简单来说,在react产生之前,关于前端的性能有两个比较大的问题

1,如何同时渲染出大量的数据

2,在用户请求时,网络是有延时的,访问的数据必须要在一段时间后才会回来,也就是说无法同步响应

浏览器的渲染是和GPU有关的,而浏览器的渲染是每秒60帧,也就是一帧为16.6ms,在这一段事件里,浏览器要做这些事

js执行-------回流-------重绘
具体可以参考event loop与重绘回流过程的关系

JS的执行是需要时间的,如果同时要执行大量的数据,JS的执行时间可能会很长,或许会超过16.6毫秒,那么这个时候可能会阻塞后面的页面渲染,造成页面的卡顿,因为渲染是有gpu完成的,所以也为gpu瓶颈

用户请求时,如果页面请求的数据没有回来,就无法完成之后的渲染,这里属于I/O瓶颈

2. 解决方案

对于GPU瓶颈,react将一帧的渲染时间进行分片,将分给JS的执行时间为5ms,如果JS在这一时间段内并没有执行完成,则会中断JS的执行,将渲染的控制权交给ui引擎,直到下一帧中在再执行之前中断的js。(时间切片)

对于I/O瓶颈,请求时间是客观存在的,并且前端难以改变,react将解决方案放在了用户感知的部分,例如在页面进行路由跳转时,如果请求时间很少,用户是无法感知的,如果请求时间较长,可以设置loading来缓解尴尬,但是如果不设置时间的临界值,只要跳转就有loading,那么其实请求的时间比较短,loading的一闪而过也会造成页面闪烁的问题,所以就有了suspense
suspense具体可以参考react 路由懒加载

React为了解决CPU的瓶颈与IO的瓶颈的理念是将同步的更新变为可中断的异步更新。

二、react15和react16的过渡

因为fiber的产生是由于react15架构有比较大的缺陷,因此在react16 提出了fiber架构

1.react15

react15 可以分为两层:

协调器(Reconciler)和渲染器(Renderer)

先说说协调器的问题,在组件进行更新时,协调器会
调用组件的render方法,将jsx转换成虚拟dom,然后利用diff算法,通过对比当前虚拟dom和之前的虚拟dom,找到需要更新的dom,通知渲染器进行更新,这里要注意的是,每发现一个变化的dom,就会去通知渲染器去渲染
个人感觉这种同步更新的方法,有点像vue的双向绑定,但是思路不同

再简单说说渲染器,渲染器收到通知后,会先选择平台,然后进行相应的渲染,网页用的就是react dom,原生app就是react native…
这里就引出了虚拟dom与真实dom相比的一个优点,支持跨平台开发,方便加壳操作

react15的缺点也很明显,react在进行渲染是,节点是通过递归查找的,一旦开始递归,就不会进行中断,如果一个节点的层级很深,可能在16.6ms内无法渲染完成,造成卡顿,同时,如果发现一个渲染一个,页面在16.6ms内有可能只会渲染部分节点内容

2.react16

react16 可以分为三层:
调度器(Scheduler),协调器(Reconciler)和渲染器(Renderer)

调度器可以区分任务的优先级,让紧急的任务先执行,在react16中,js未执行完成可以先中断,作为过期任务放在调度器中,等待下一帧执行,一般来说越早过期优先级越高

同时协调器也会有所不同,在递归寻找元素时,每次循环都会调用shouldyield,查看浏览器分给js的执行时间切片是否还有剩余的时间,如果没有,就中断,等待下一次执行,同时协调器在发现要改变的虚拟dom后,会先做出不同的标记(增删改),然后查询完成后一起交给renderer进行一次性的同步渲染
个人感觉也与这个就是setstate异步渲染的原因吧

那么怎么去给虚拟dom做标记,如何渲染新的dom就要fiber登场啦

三、fiber

1.fiber工作原理

我们可以理解当前的页面是来源于一个fiber tree(称为currentfiber tree),而页面里面的每一个元素都对应树里面的每一个fiber节点,当我们页面更新时,会在内存中新建一个fiber树(称为workinprogressfiber tree),然后新的fiber树代替旧的fiber树,然后呈现出新的页面(双缓存Fiber树

这里要注意的是他们的alternate指向对方

currentFiber.alternate === workInProgressFiber;
workInProgressFiber.alternate === currentFiber;

在页面加载时,页面会创建两个树,fiberroot tree 和rootfiber tree,前者是整个项目的根节点,后者是组件的根节点,fiberroot 的current属性将指向currentfiber tree

function App() {
  const [num, add] = useState(0);
  return (
    <p onClick={() => add(num + 1)}>{num}</p>
  )
}

ReactDOM.render(<App/>, document.getElementById('root'));

在首页渲染时,因为rootfiber tree中无任何子节点,所以为空,当执行了render后,在内存中生成了workInProgressFiber,这个树会复用之前的rootfiber的属性和节点,这里只能复用alternate,同时他包含了‘’app,p’这些节点,
react fiber摘要
至此,workinprogress tree创建完成,进入commit阶段,浏览器会渲染新的fiber tree,渲染完成,fiberroot tree 的current指向新树,成为currentfiner tree

当组件更新时,例如点击一次p,触发render,在内存中生成了新的workInProgressFiber tree,这个树会尝试复用之前currentrfiber tree的属性和节点,至于复用哪些属性和元素,要用diff算法决定,workinprogress tree创建完成,进入commit阶段,浏览器会渲染新的fiber tree,渲染完成,fiberroot tree 的current指向新树,成为currentfiner treereact fiber摘要

1.fiber记录操作状态

上文说到,fiber树有fiber节点构成,当fiber节点状态改变时,对应的fiber节点就会有属性变化保存操作状态供渲染器操作

// 保存本次更新造成的状态改变相关信息
this.pendingProps = pendingProps;
this.memoizedProps = null;
this.updateQueue = null;
this.memoizedState = null;
this.dependencies = null;

四、参考文章

1,前端面试之道
2,React技术揭秘
3,React 源码 Scheduler(一)浏览器的调度

上一篇:分享最近学习react源码的经历


下一篇:18-2 djanjo中间件和orm多对多操作,以及ajax