一、前言
在 React Fiber 架构面世一年多后,最近 React 又公布了最新版 16.8.0,又一激动人心的个性:React Hooks 正式上线,让我降级 React 的志愿越来越强烈了。在降级之前,无妨回到原点,理解下人才济济的 React 团队为什么要大费周章,重写 React 架构,而 Fiber 又是个什么概念。
二、React 15 的问题
在页面元素很多,且须要频繁刷新的场景下,React 15 会呈现掉帧的景象。请看以下例子:
https://claudiopro.github.io/…
其根本原因,是大量的同步计算工作阻塞了浏览器的 UI 渲染。默认状况下,JS 运算、页面布局和页面绘制都是运行在浏览器的主线程当中,他们之间是互斥的关系。如果 JS 运算继续占用主线程,页面就没法失去及时的更新。当咱们调用 setState 更新页面的时候,React 会遍历利用的所有节点,计算出差别,而后再更新 UI。整个过程是零打碎敲,不能被打断的。如果页面元素很多,整个过程占用的机会就可能超过 16 毫秒,就容易呈现掉帧的景象。
针对这一问题,React 团队从框架层面对 web 页面的运行机制做了优化,失去很好的成果。
三、解题思路
解决主线程长时间被 JS 运算占用这一问题的基本思路,是将运算切割为多个步骤,分批实现。也就是说在实现一部分工作之后,将控制权交回给浏览器,让浏览器有工夫进行页面的渲染。等浏览器忙完之后,再持续之前未实现的工作。
旧版 React 通过递归的形式进行渲染,应用的是 JS 引擎本身的函数调用栈,它会始终执行到栈空为止。而 Fiber 实现了本人的组件调用栈,它以链表的模式遍历组件树,能够灵便的暂停、持续和抛弃执行的工作。实现形式是应用了浏览器的 requestIdleCallback 这一 API。官网的解释是这样的:
window.requestIdleCallback()会在浏览器闲暇期间顺次调用函数,这就能够让开发者在主事件循环中执行后盾或低优先级的工作,而且不会对像动画和用户交互这些提早触发但要害的事件产生影响。函数个别会按先进先调用的程序执行,除非函数在浏览器调用它之前就到了它的超时工夫。
有了解题思路后,咱们再来看看 React 具体是怎么做的。
四、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 运作的过程是不能被打断的,必须一条道走到黑:
而 Fiber Reconciler 每执行一段时间,都会将控制权交回给浏览器,能够分段执行:
为了达到这种成果,就须要有一个调度器 (Scheduler) 来进行任务分配。工作的优先级有六种:
- synchronous,与之前的 Stack Reconciler 操作一样,同步执行
- task,在 next tick 之前执行
- animation,下一帧之前执行
- high,在不久的未来立刻执行
- low,略微提早执行也没关系
- offscreen,下一次 render 时或 scroll 时才执行
优先级高的工作(如键盘输入)能够打断优先级低的工作(如 Diff)的执行,从而更快的失效。
Fiber Reconciler 在执行过程中,会分为 2 个阶段。
阶段一,生成 Fiber 树,得出须要更新的节点信息。这一步是一个渐进的过程,能够被打断。
阶段二,将须要更新的节点一次过批量更新,这个过程不能被打断。
阶段一可被打断的个性,让优先级更高的工作先执行,从框架层面大大降低了页面掉帧的概率。
五、Fiber 树
Fiber Reconciler 在阶段一进行 Diff 计算的时候,会生成一棵 Fiber 树。这棵树是在 Virtual DOM 树的根底上减少额定的信息来生成的,它实质来说是一个链表。
- Fiber 树在首次渲染的时候会一次过生成。在后续须要 Diff 的时候,会依据已有树和最新 – Virtual DOM 的信息,生成一棵新的树。这颗新树每生成一个新的节点,都会将控制权交回给主线程,去查看有没有优先级更高的工作须要执行。如果没有,则持续构建树的过程:
如果过程中有优先级更高的工作须要进行,则 Fiber Reconciler 会抛弃正在生成的树,在闲暇的时候再从新执行一遍。
在结构 Fiber 树的过程中,Fiber Reconciler 会将须要更新的节点信息保留在 Effect List 当中,在阶段二执行的时候,会批量更新相应的节点。
六、总结
本文从 React 15 存在的问题登程,介绍 React Fiber 解决问题的思路,并介绍了 Fiber Reconciler 的工作流程。从 Stack Reconciler 到 Fiber Reconciler,源码层面其实就是干了一件递归改循环的事件,日后有机会的话,我再联合源码作进一步的介绍。