# Fiber双缓冲 > 面试题:谈一谈你对 React 中 Fiber 的理解以及什么是 Fiber 双缓冲? ## 对 Fiber 的理解 实际上,我们可以从三个维度来理解 Fiber: - 是一种架构,称之为 Fiber 架构 - 是一种数据类型 - 动态的工作单元 **是一种架构,称之为 Fiber 架构** 在 React v16之前,使用的是 Stack Reconciler,因此那个时候的 React 架构被称之为 Stack 架构。从 React v16 开始,重构了整个架构,引入了 Fiber,因此新的架构也被称之为 Fiber 架构,Stack Reconciler 也变成了 Fiber Reconciler。各个FiberNode之间通过链表的形式串联起来: ```js function FiberNode(tag, pendingProps, key, mode) { // ... // 周围的 Fiber Node 通过链表的形式进行关联 this.return = null; this.child = null; this.sibling = null; this.index = 0; // ... } ``` **是一种数据类型** Fiber 本质上也是一个对象,是在之前 React 元素基础上的一种升级版本。每个 FiberNode 对象里面会包含 React 元素的类型、周围链接的FiberNode以及 DOM 相关信息: ```js function FiberNode(tag, pendingProps, key, mode) { // 类型 this.tag = tag; this.key = key; this.elementType = null; this.type = null; this.stateNode = null; // 映射真实 DOM // ... } ``` **动态的工作单元** 在每个 FiberNode 中,保存了本次更新中该 React 元素变化的数据,还有就是要执行的工作(增、删、更新)以及副作用的信息: ```js function FiberNode(tag, pendingProps, key, mode) { // ... // 副作用相关 this.flags = NoFlags; this.subtreeFlags = NoFlags; this.deletions = null; // 与调度优先级有关 this.lanes = NoLanes; this.childLanes = NoLanes; // ... } ``` > 为什么指向父 FiberNode 的字段叫做 return 而非 parent? > > 因为作为一个动态的工作单元,return 指代的是 FiberNode 执行完 completeWork 后返回的下一个 FiberNode,这里会有一个返回的动作,因此通过 return 来指代父 FiberNode ## Fiber 双缓冲 Fiber 架构中的双缓冲工作原理类似于显卡的工作原理。 显卡分为前缓冲区和后缓冲区。首先,前缓冲区会显示图像,之后,合成的新的图像会被写入到后缓冲区,一旦后缓冲区写入图像完毕,就会前后缓冲区进行一个互换,这种将数据保存在缓冲区再进行互换的技术,就被称之为双缓冲技术。 Fiber 架构同样用到了这个技术,在 Fiber 架构中,同时存在两颗 Fiber Tree,一颗是真实 UI 对应的 Fiber Tree,可以类比为显卡的前缓冲区,另外一颗是在内存中构建的 FiberTree,可以类比为显卡的后缓冲区。 在 React 源码中,很多方法都需要接收两颗 FiberTree: ```js function cloneChildFibers(current, workInProgress){ // ... } ``` current 指的就是前缓冲区的 FiberNode,workInProgress 指的就是后缓冲区的 FiberNode。 两个 FiberNode 会通过 alternate 属性相互指向: ```js current.alternate = workInProgress; workInProgress.alternate = current; ``` 接下来我们从首次渲染(mount)和更新(update)这两个阶段来看一下 FiberTree 的形成以及双缓存机制: **mount 阶段** 首先最顶层有一个 FiberNode,称之为 FiberRootNode,该 FiberNode 会有一些自己的任务: - Current Fiber Tree 与 Wip Fiber Tree 之间的切换 - 应用中的过期时间 - 应用的任务调度信息 现在假设有这么一个结构: ```html
``` ```jsx function App(){ const [num, add] = useState(0); return (add(num + 1)}>{num}
); } const rootElement = document.getElementById("root"); ReactDOM.createRoot(rootElement).render(
此时会有一个 HostRootFiber,FiberRootNode 通过 current 来指向 HostRootFiber。
接下来进入到 mount 流程,该流程会基于每个 React 元素以深度优先的原则依次生成 wip FiberNode,并且每一个 wipFiberNode 会连接起来,如下图所示:
生成的 wip FiberTree 里面的每一个 FiberNode 会和 current FiberTree 里面的 FiberNode进行关联,关联的方式就是通过 alternate。但是目前 currentFiberTree里面只有一个 HostRootFiber,因此就只有这个 HostRootFiber 进行了 alternate 的关联。
当 wip FiberTree生成完毕后,也就意味着 render 阶段完毕了,此时 FiberRootNode就会被传递给 Renderer(渲染器),接下来就是进行渲染工作。渲染工作完毕后,浏览器中就显示了对应的 UI,此时 FiberRootNode.current 就会指向这颗 wip Fiber Tree,曾经的 wip Fiber Tree 它就会变成 current FiberTree,完成了双缓存的工作:
**update 阶段**
点击 p 元素,会触发更新,这一操作就会开启 update 流程,此时就会生成一颗新的 wip Fiber Tree,流程和之前是一样的
新的 wip Fiber Tree 里面的每一个 FiberNode 和 current Fiber Tree 的每一个 FiberNode 通过 alternate 属性进行关联。
当 wip Fiber Tree 生成完毕后,就会经历和之前一样的流程,FiberRootNode 会被传递给 Renderer 进行渲染,此时宿主环境所渲染出来的真实 UI 对应的就是左边 wip Fiber Tree 所对应的 DOM 结构,FiberRootNode.current 就会指向左边这棵树,右边的树就再次成为了新的 wip Fiber Tree
这个就是 Fiber双缓存的工作原理。
另外值得一提的是,开发者是可以在一个页面创建多个应用的,比如:
```js
ReactDOM.createRoot(rootElement1).render(