Published on

React Fiber 双缓冲

Authors
  • avatar
    Name
    Et cetera
    Twitter

Fiber 双缓冲

面试题:谈一谈你对 React 中 Fiber 的理解以及什么是 Fiber 双缓冲?

对 Fiber 的理解

实际上,我们可以从三个维度来理解 Fiber

  • 是一种架构,称之为 Fiber 架构
  • 是一种数据类型
  • 动态的工作单元

是一种架构,称之为 Fiber 架构

React v16 之前,使用的是 Stack Reconciler,因此那个时候的 React 架构被称之为 Stack 架构.从 React v16 开始,重构了整个架构,引入了 Fiber,因此新的架构也被称之为 Fiber 架构,Stack Reconciler 也变成了 Fiber Reconciler.各个 FiberNode 之间通过链表的形式串联起来:

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 相关信息:

function FiberNode(tag, pendingProps, key, mode) {
  // 类型
  this.tag = tag
  this.key = key
  this.elementType = null
  this.type = null
  this.stateNode = null // 映射真实 DOM

  // ...
}

动态的工作单元

在每个 FiberNode 中,保存了本次更新中该 React 元素变化的数据,还有就是要执行的工作(增、删、更新)以及副作用的信息:

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

function cloneChildFibers(current, workInProgress) {
  // ...
}

current 指的就是前缓冲区的 FiberNodeworkInProgress 指的就是后缓冲区的 FiberNode.

两个 FiberNode 会通过 alternate 属性相互指向:

current.alternate = workInProgress
workInProgress.alternate = current

接下来我们从首次渲染(mount)更新(update) 这两个阶段来看一下 FiberTree 的形成以及双缓存机制:

mount 阶段

首先最顶层有一个 FiberNode,称之为 FiberRootNode,该 FiberNode 会有一些自己的任务:

  • Current Fiber TreeWip Fiber Tree 之间的切换
  • 应用中的过期时间
  • 应用的任务调度信息

现在假设有这么一个结构:

<body>
  <div id="root"></div>
</body>
function App() {
  const [num, add] = useState(0)
  return <p onClick={() => add(num + 1)}>{num}</p>
}
const rootElement = document.getElementById('root')
ReactDOM.createRoot(rootElement).render(<App />)

当执行 ReactDOM.createRoot 的时候,会创建如下的结构:

此时会有一个 HostRootFiberFiberRootNode 通过 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 里面的每一个 FiberNodecurrent Fiber Tree 的每一个 FiberNode 通过 alternate 属性进行关联.

wip Fiber Tree 生成完毕后,就会经历和之前一样的流程,FiberRootNode 会被传递给 Renderer 进行渲染,此时宿主环境所渲染出来的真实 UI 对应的就是左边 wip Fiber Tree 所对应的 DOM 结构,FiberRootNode.current 就会指向左边这棵树,右边的树就再次成为了新的 wip Fiber Tree

这个就是 Fiber 双缓存的工作原理.

另外值得一提的是,开发者是可以在一个页面创建多个应用的,比如:

ReactDOM.createRoot(rootElement1).render(<App1 />)
ReactDOM.createRoot(rootElement2).render(<App2 />)
ReactDOM.createRoot(rootElement3).render(<App3 />)

在上面的代码中,我们创建了 3 个应用,此时就会存在 3 个 FiberRootNode,以及对应最多 6 棵 Fiber Tree 树.

真题解析

题目:谈一谈你对 React 中 Fiber 的理解以及什么是 Fiber 双缓冲?参考答案:Fiber 可以从三个方面去理解:- FiberNode 作为一种架构:在 React v15 以及之前的版本中,Reconceiler 采用的是递归的方式,因此被称之为 Stack Reconciler,到了 React v16 版本之后,引入了 Fiber,Reconceiler 也从 Stack Reconciler 变为了 Fiber Reconceiler,各个 FiberNode 之间通过链表的形式串联了起来.- FiberNode 作为一种数据类型:Fiber 本质上也是一个对象,是之前虚拟 DOM 对象(React 元素,createElement 的返回值)的一种升级版本,每个 Fiber 对象里面会包含 React 元素的类型,周围链接的 FiberNode,DOM 相关信息.- FiberNode 作为动态的工作单元:在每个 FiberNode 中,保存了“本次更新中该 React 元素变化的数据、要执行的工作(增、删、改、更新 Ref、副作用等)”等信息.所谓 Fiber 双缓冲树,指的是在内存中构建两颗树,并直接在内存中进行替换的技术.在 React 中使用 Wip Fiber Tree 和 Current Fiber Tree 这两颗树来实现更新的逻辑.Wip Fiber Tree 在内存中完成更新,而 Current Fiber Tree 是最终要渲染的树,两颗树通过 alternate 指针相互指向,这样在下一次渲染的时候,直接复用 Wip Fiber Tree 作为下一次的渲染树,而上一次的渲染树又作为新的 Wip Fiber Tree,这样可以加快 DOM 节点的替换与更新.