如何理解 Fiber 架构的迭代动机与设计思想
在 React 16.x 版本中,其核心的 Diff 算法被重写,以'Fiber Reconciler'的全新面貌示人。
Stack Reconciler 存在根深蒂固的局限性,使得 React 不得不从架构层面做出改变。Fiber 架构基于此实现了调和过程的不同。
前置知识:单线程的 JavaScript 与多线程的浏览器
JavaScript 是单线程的,浏览器是多线程的。浏览器除了处理 JavaScript 线程外,还需处理事件系统、定时器、网络请求及 DOM 渲染线程。JavaScript 线程可以操作 DOM,因此 JavaScript 线程和渲染线程必须是互斥的,不能穿插执行,必须串行。当其中一个线程执行时,另一个线程只能挂起等待。
若 JavaScript 线程长时间占用主线程,渲染层面的更新会长时间等待,导致界面卡顿。事件线程也在等待 JavaScript,导致触发的事件难以被响应。
为什么会产生'卡顿'这样的困局
Stack Reconciler 带来的无解问题是 JavaScript 对主线程的超时占用问题。Stack Reconciler 是一个同步的递归过程,一旦更新开始便不可中断。
在 React 15 及之前的版本中,虚拟 DOM 树的数据结构载体是'树',Diff 算法沿袭了传统计算机科学中'对比两棵树'的算法,本质上是树的深度优先遍历过程。若 A 组件发生更新,栈调和的工作过程是对比节点,确认可复用后继续 Diff 子组件,直到最深一层节点更新完毕才向上返回。
这个过程的致命性在于它是同步的,不可以被打断。当处理结构复杂、体量庞大的虚拟 DOM 树时,调和时间长,JavaScript 线程将长时间霸占主线程,导致渲染卡顿或卡死。
设计思想:Fiber 是如何解决问题的
Fiber 意为'丝、纤维',是比线还要细的东西。在计算机科学里,Fiber 是比线程更纤细的过程,即'纤程'。纤程的出现意在对渲染过程实现更加精细的控制。
Fiber 是一个多义词:
- 从架构角度看,Fiber 是对 React 核心算法(即调和过程)的重写;
- 从编码角度看,Fiber 是 React 内部定义的一种数据结构,是 Fiber 树结构的节点单位,即 React 16 新架构下的'虚拟 DOM';
- 从工作流角度看,Fiber 节点保存了组件需要更新的状态和副作用,一个 Fiber 同时也对应着一个工作单元。
Fiber 架构的应用目的是实现'增量渲染'。所谓增量渲染,就是把一个渲染任务分解为多个渲染任务,分散到多个帧里面。实现增量渲染的目的,是为了实现任务的可中断、可恢复,并给不同的任务赋予不同的优先级,最终达成更加顺滑的用户体验。
Fiber 架构核心:'可中断''可恢复'与'优先级'
在 React 16 之前,React 的渲染和更新阶段依赖两层架构:Reconciler 层负责对比新老虚拟 DOM 之间的变化,Renderer 层负责将变化的部分应用到视图上,从 Reconciler 到 Renderer 这个过程是严格同步的。
而在 React 16 中,为了实现'可中断'和'优先级',两层架构变成了三层架构:
- Scheduler(调度器):调度更新的优先级。
在这套架构模式下,更新的处理工作流如下:首先,每个更新任务都会被赋予一个优先级。当更新任务抵达调度器时,高优先级的更新任务会更快地被调度进 Reconciler 层;此时若有新的更新任务抵达调度器,调度器会检查它的优先级,若发现新任务优先级高于当前任务,那么当前处于 Reconciler 层的任务就会被中断,调度器会将新任务推入 Reconciler 层。当新任务完成渲染后,新一轮的调度开始,之前被中断的任务将会被重新推入 Reconciler 层,继续它的渲染之旅,这便是所谓'可恢复'。

在 React 15 及之前的版本中,虚拟 DOM 树的数据结构载体是计算机科学中的'树',其 Diff 算法的遍历思路,也是沿袭了传统计算机科学中'对比两棵树'的算法,在此基础上优化得来。因此从本质上来说,栈调和机制下的 Diff 算法,其实是树的深度优先遍历的过程。而树的深度优先遍历,总是和递归脱不了关系。





