# 双缓存 fiber 树

# 什么是双缓存?

前面说了 React 最多会同时存在两棵 Fiber 树,那么什么是 双缓存呢?

当我们用 canvas 绘制动画,每一帧绘制前都会调用 ctx.clearRect 清除上一帧的画面。

如果当前帧画面计算量比较大,导致清除上一帧画面到绘制当前帧画面之间有较长间隙,就会出现白屏。

为了解决这个问题,我们可以在内存中绘制当前帧动画,绘制完毕后直接用当前帧替换上一帧画面,由于省去了两帧替换间的计算时间,不会出现从白屏到出现画面的闪烁情况。

这种 在内存中构建并直接替换 的技术叫做 双缓存。

React 使用“双缓存”来完成 Fiber 树的构建与替换——对应着 DOM 树 的创建与更新。

# 双缓存 Fiber 树

在 React 中最多会同时存在两棵 Fiber 树。当前屏幕上显示内容对应的 Fiber 树称为 current Fiber 树,正在内存中构建的 Fiber 树称为 workInProgress Fiber 树。他们通过 alternate 属性连接

currentFiber.alternate === workInProgressFiber;
workInProgressFiber.alternate === currentFiber;
1
2

React 应用的根节点通过 current 指针在不同的 Fiber 树的 rootFiber 间切换来实现 Fiber 树的切换

当 workInProgress Fiber 树构建完成交给 Renderer 渲染在页面上后,应用根节点的 current 指针指向 workInProgress Fiber 树,此时 workInProgress Fiber 树就变为 current Fiber 树。

每次状态更新都会产生新的 workInProgress Fiber 树,通过 current 与 workInProgress 的替换,完成 DOM 更新。

# mount

  1. 首次执行 ReactDOM.render 会创建 fiberRootNode(源码中叫 fiberRoot)和 rootFiber。其中 fiberRootNode 是整个应用的根节点,rootFiber 是所在组件树的根节点。 会调用 createContainer 创建 FiberRoot,并挂载在 root 的 current 上

之所以要区分 fiberRootNode 与 rootFiber,是因为在应用中我们可以多次调用 ReactDOM.render 渲染不同的组件树,他们会拥有不同的 rootFiber。但是整个应用的根节点只有一个,那就是 fiberRootNode。

// 创建第一个FiberNode
const uninitializedFiber = createHostRootFiber(tag);
root.current = uninitializedFiber;
uninitializedFiber.stateNode = root;
1
2
3
4

由于是首屏渲染,页面中还没有挂载任何 DOM,所以 fiberRootNode.current 指向的 rootFiber 没有任何子 Fiber 节点(即 current Fiber 树为空)。

  1. 接下来进入 render 阶段,根据组件返回的 JSX 在内存中依次创建 Fiber 节点并连接在一起构建 Fiber 树,被称为 **workInProgress Fiber **树。(下图中右侧为内存中构建的树,左侧为页面显示的树)

在构建 workInProgress Fiber 树时会尝试复用 current Fiber 树中已有的 Fiber 节点内的属性,在首屏渲染时只有 rootFiber 存在对应的 current fiber(即 rootFiber.alternate)。

  1. 右侧已构建完的 workInProgress Fiber 树在 commit 阶段渲染到页面。此时 DOM 更新为右侧树对应的样子。fiberRootNode 的 current 指针指向 workInProgress Fiber 树使其变为 current Fiber 树。

# update

接下来我们点击 p 节点触发状态改变,这会开启一次新的 render 阶段并构建一棵新的 workInProgress Fiber 树。

和 mount 时一样,workInProgress fiber 的创建可以复用 current Fiber 树对应的节点数据。

这个决定是否复用的过程就是 Diff 算法

workInProgress Fiber 树在 render 阶段完成构建后进入 commit 阶段渲染到页面上。渲染完毕后,workInProgress Fiber 树变为 current Fiber 树 也就是第一张图那张。