React Fiber 原理介绍

欢迎关注个人公众号睿Talk,获取我最新的文章:
clipboard.pngjavascript

1、前言

在 React Fiber 架构面世一年多后,最近 React 又发布了最新版 16.8.0,又一激动人心的特性:React Hooks 正式上线,让我升级 React 的意愿愈来愈强烈了。在升级以前,不妨回到原点,了解下人才辈出的 React 团队为何要大费周章,重写 React 架构,而 Fiber 又是个什么概念。java

2、React 15 的问题

在页面元素不少,且须要频繁刷新的场景下,React 15 会出现掉帧的现象。请看如下例子:
https://claudiopro.github.io/...react

clipboard.png

其根本缘由,是大量的同步计算任务阻塞了浏览器的 UI 渲染。默认状况下,JS 运算、页面布局和页面绘制都是运行在浏览器的主线程当中,他们之间是互斥的关系。若是 JS 运算持续占用主线程,页面就无法获得及时的更新。当咱们调用setState更新页面的时候,React 会遍历应用的全部节点,计算出差别,而后再更新 UI。整个过程是一鼓作气,不能被打断的。若是页面元素不少,整个过程占用的时机就可能超过 16 毫秒,就容易出现掉帧的现象。git

针对这一问题,React 团队从框架层面对 web 页面的运行机制作了优化,获得很好的效果。github

clipboard.png

3、解题思路

解决主线程长时间被 JS 运算占用这一问题的基本思路,是将运算切割为多个步骤,分批完成。也就是说在完成一部分任务以后,将控制权交回给浏览器,让浏览器有时间进行页面的渲染。等浏览器忙完以后,再继续以前未完成的任务。web

旧版 React 经过递归的方式进行渲染,使用的是 JS 引擎自身的函数调用栈,它会一直执行到栈空为止。而Fiber实现了本身的组件调用栈,它以链表的形式遍历组件树,能够灵活的暂停、继续和丢弃执行的任务。实现方式是使用了浏览器的requestIdleCallback这一 API。官方的解释是这样的:segmentfault

window.requestIdleCallback()会在浏览器空闲时期依次调用函数,这就可让开发者在主事件循环中执行后台或低优先级的任务,并且不会对像动画和用户交互这些延迟触发但关键的事件产生影响。函数通常会按先进先调用的顺序执行,除非函数在浏览器调用它以前就到了它的超时时间。

有了解题思路后,咱们再来看看 React 具体是怎么作的。浏览器

4、React 的答卷

React 框架内部的运做能够分为 3 层:数据结构

  • Virtual DOM 层,描述页面长什么样。
  • Reconciler 层,负责调用组件生命周期方法,进行 Diff 运算等。
  • Renderer 层,根据不一样的平台,渲染出相应的页面,比较常见的是 ReactDOM 和 ReactNative。

此次改动最大的当属 Reconciler 层了,React 团队也给它起了个新的名字,叫Fiber Reconciler。这就引入另外一个关键词:Fiber。架构

Fiber 其实指的是一种数据结构,它能够用一个纯 JS 对象来表示:

const fiber = {
    stateNode,    // 节点实例
    child,        // 子节点
    sibling,      // 兄弟节点
    return,       // 父节点
}

为了加以区分,之前的 Reconciler 被命名为Stack Reconciler。Stack Reconciler 运做的过程是不能被打断的,必须一条道走到黑:

clipboard.png

而 Fiber Reconciler 每执行一段时间,都会将控制权交回给浏览器,能够分段执行:

clipboard.png

为了达到这种效果,就须要有一个调度器 (Scheduler) 来进行任务分配。任务的优先级有六种:

  • synchronous,与以前的Stack Reconciler操做同样,同步执行
  • task,在next tick以前执行
  • animation,下一帧以前执行
  • high,在不久的未来当即执行
  • low,稍微延迟执行也不要紧
  • offscreen,下一次render时或scroll时才执行

优先级高的任务(如键盘输入)能够打断优先级低的任务(如Diff)的执行,从而更快的生效。

Fiber Reconciler 在执行过程当中,会分为 2 个阶段。

clipboard.png

  • 阶段一,生成 Fiber 树,得出须要更新的节点信息。这一步是一个渐进的过程,能够被打断。
  • 阶段二,将须要更新的节点一次过批量更新,这个过程不能被打断。

阶段一可被打断的特性,让优先级更高的任务先执行,从框架层面大大下降了页面掉帧的几率。

5、Fiber 树

Fiber Reconciler 在阶段一进行 Diff 计算的时候,会生成一棵 Fiber 树。这棵树是在 Virtual DOM 树的基础上增长额外的信息来生成的,它本质来讲是一个链表。

clipboard.png

Fiber 树在首次渲染的时候会一次过生成。在后续须要 Diff 的时候,会根据已有树和最新 Virtual DOM 的信息,生成一棵新的树。这颗新树每生成一个新的节点,都会将控制权交回给主线程,去检查有没有优先级更高的任务须要执行。若是没有,则继续构建树的过程:

clipboard.png

若是过程当中有优先级更高的任务须要进行,则 Fiber Reconciler 会丢弃正在生成的树,在空闲的时候再从新执行一遍。

在构造 Fiber 树的过程当中,Fiber Reconciler 会将须要更新的节点信息保存在Effect List当中,在阶段二执行的时候,会批量更新相应的节点。

6、总结

本文从 React 15 存在的问题出发,介绍 React Fiber 解决问题的思路,并介绍了 Fiber Reconciler 的工做流程。从Stack ReconcilerFiber Reconciler,源码层面其实就是干了一件递归改循环的事情,往后有机会的话,我再结合源码做进一步的介绍。

相关文章
相关标签/搜索