摘要
React Fiber是react核心算法的重构,目的在解决react渲染的性能问题。在React16版本中进行发布。本文从react为什么要重构出发,带你了解React Fiber的原理。
首先我们先来理解几个概念
- 进程:计算机分配资源的最小单元
- 线程:计算机执行任务的最小单元
- DFS(深度优先遍历) :首先从根节点开始访问,一直向下找子节点,当没有子节点就找找兄弟节点,没有兄弟节点就找叔叔节点,直到遍历完整个数据,我们用图来看一下。
- 帧: 帧就是显示器的刷新频率,普遍为60FPS。就是一秒内显示器会刷新60次,那么每一帧的刷新时间就为16.6ms
浏览器(多线程)JS(单线程)
React为什么会推出Fiber架构
在React16推出Fiber之前,react会采用深度优先遍历(DFS) 对比虚拟DOM树,找出变更的节点。然后同步更新他们,这个过程被称为协调(reconciliation)。当Dom的节点非常深的时候,在reconciliation期间,react会一直占用浏览器资源,占用的时间超过了16.6ms也就是一帧的时间,就会造成页面的卡顿。
因此,react希望能解决长时间占用主线程的问题,引入了Fiber。把渲染、更新过程拆分为一个个小块的任务,让reconciliation过程变的可中断,通过合理的调度机制调控时间,指定任务执行的时机,从而降低页面卡顿的概率。
什么是Fiber
Fiber可以理解为一个执行单元,也可以理解为一种数据结构。
执行单元
执行单元就是react把一个大的任务拆分成一个个的小块任务,每一个小块任务是在一次执行必须完成的,每一个小块的任务执行完成后会向浏览器询问是否还有多余的时间,如果没有多余的时间就把控制权交回给浏览器,如果还有多余的时间就继续执行下一个任务块。
数据结构
Fiber还可以理解成一种数据结构,React Fiber就是采用链表实现的。每个节点都可以表示为一个Fiber,一个Fiber包含了child(第一个子节点),sibling(兄弟节点),return(父节点) 等属性。
Fiber执行原理
从根节点开始渲染和调度主要有以下几个阶段:render阶段、commit阶段。
- render阶段:这个阶段是可中断的,会找出所有节点的变更
- commit阶段:这个阶段是不可中断的,会执行所有的变更。
render阶段
此阶段会找出所有的变更,如节点新增、删除、属性变更等,这个变更被react统称为副作用(effect)。
此阶段会构建一颗Fiber树,以虚拟DOM为维度对任务进行拆分,即一个虚拟DOM为一个任务,最后产出的结果是effect list,从中可以知道节点的增加、删除、更新。 还会创建一些全局变量,如workInProgressRoot、nextUnitOfWork,用于记录当前的工作任务和工作单元。
单元执行
在这里我们就可以引入一个requestAnimationFrame和requestIdleCallback的api。
- requestAnimationFrame:一帧内必须执行的函数
- requestIdleCallback:一帧内如果有空闲时间就执行的函数
requestAnimationFrame
window.requestAnimationFrame() 告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行MDN链接
requestIdleCallback
window.requestIdleCallback() 方法插入一个函数,这个函数将在浏览器空闲时期被调用。这使开发者能够在主事件循环上执行后台和低优先级工作,而不会影响延迟关键事件,如动画和输入响应。函数一般会按先进先调用的顺序执行,然而,如果回调函数指定了执行超时时间timeout,则有可能为了在超时前执行函数而打乱执行顺序。MDN链接
requestIdleCallback也是之前 react Fiber 实现的基础 api,为什么说之前呢,看下面来自github facebook/react issue的截图
React自己实现了一个。它利用messagechannel将回调延迟到绘制操作之后执行MDN链接
commit阶段
此阶段需要把上一步算出的所有副作用一次更新到DOM树上,此阶段不能暂停,否则会出现UI更新不连续的现象
总结
看完希望大家对React Fiber有一个详细的了解,有什么问题可以随时在评论区进行讨论,或者私聊我,我都会尽力回复。一起进步