Fiber 背景
首先要知道的是,JS 引擎和页面渲染引擎两个线程是互斥的
,当其中一个线程执行时,另一个线程只能挂起等待
。在这样的机制下,如果 JS 线程长时间地占用了主线程,那么渲染层面的更新就不得不长时间地等待,界面长时间不更新,会导致页面响应度变差,用户可能会感觉到卡顿
而这正是 React15 的 Stack Reconciler 所面临的问题
,即是 JS 对主线程的超时占用问题。Stack Reconciler 是一个同步的递归过程
,使用的是 JS 引擎自身的函数调用栈
,它会一直执行到栈空为止,所以当 React 在渲染组件时,从开始到渲染完成整个过程是一气呵成的。如果渲染的组件比较庞大,JS 执行会占据主线程较长时间,会导致页面响应度变差
。而且所有的任务都是按照先后顺序,没有区分优先级,这样就会导致优先级比较高的任务无法被优先执行
Fiber 概念
Fiber 与进程、线程同为程序执行过程,Fiber 就是比线程还要纤细的一个过程,意味着在对渲染过程进行更加精细的控制
从架构角度看,Fiber 是对 React 核心算法(即调度过程)的重写
-
Scheduler(调度器)—— 调度任务的优先级,高优任务优先进入Reconciler
-
Reconciler(协调器)—— 负责找出变化的组件
-
Renderer(渲染器)—— 负责将变化的组件渲染到页面上
从编码角度来看,Fiber 是 React 内部所定义的一种数据结构(链表),它是 Fiber 树结构的节点单位,也就是 React 16 新架构下的虚拟 DOM
Fiber 是如何解决问题的
Fiber 把一个渲染任务分解为多个渲染任务,而不是一次性完成,把每一个分割得很细得任务视作一个执行单元
,React 就会检查现在还剩多少时间,如果没有时间就将控制权让出去,故任务会被分散到多个帧里面,中间可以返回至主程控制执行其他任务
,最终实现更流畅的用户体验。
即实现了增量渲染,
实现可中断与恢复
,恢复后也可以复用之前的中间状态
,并给不同的任务赋予不同的优先级,其中每个任务更新单元为 React Element 对应的 Fiber 节点。
Fiber 实现原理
实现的方式是 requestIdleCallback
这一 api,但 React 团队 polyfill 了这个 api,使其对比原生的浏览器兼容性更好且扩展了这个特性
。requestIdleCallback 回调的执行前提条件是当前浏览器处于空闲状态
。即 requestIdleCallback 的作用是在浏览器一帧的剩余空闲时间内执行优先度相对较低的任务
。
-
首先
React 中任务切割为多个步骤,分批完成
。在完成一部分任务之后,将控制权交回浏览器,让浏览器有时间在进行页面的渲染。等浏览器忙完之后有剩余时间,在继续之前 React 未完成的任务,是一种合作式调度。简而言之,由浏览器给我们分配执行时间片
,我们要按照约定在这个时间内执行完毕,并将控制权交还给浏览器
。React16 的 Reconciler 基于 Fiber 节点实现,被称为 Fiber Reconciler -
作为静态的数据结构来说,
每个 Fiber 节点对应一个 React element
,保存该组件的类型(函数组件、类组件、原生组件等等),对应的 DOM 节点等信息 -
作为动态的工作单元来说,每
个 Fiber 节点保存本次更新中该组件改变的状态、要执行的工作
。每个 Fiber 节点有个对应的 React element,多个 Fiber 节点是如何形成树的呢?靠下面的三个属性:
//指向父级Fiber节点
this.return = null
// 指向子Fiber节点
this.child = null
// 指向右边第一个兄弟Fiber节点
this.sibling = null