- Published on
React Fiber 双缓冲
- Authors
- Name
- Et cetera
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
指的就是前缓冲区的 FiberNode
,workInProgress
指的就是后缓冲区的 FiberNode
.
两个 FiberNode
会通过 alternate
属性相互指向:
current.alternate = workInProgress
workInProgress.alternate = current
接下来我们从首次渲染(mount)
和更新(update)
这两个阶段来看一下 FiberTree
的形成以及双缓存机制:
mount 阶段
首先最顶层有一个 FiberNode
,称之为 FiberRootNode
,该 FiberNode
会有一些自己的任务:
Current Fiber Tree
与Wip 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
的时候,会创建如下的结构:
此时会有一个 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 双缓存的工作原理.
另外值得一提的是,开发者是可以在一个页面创建多个应用的,比如:
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 节点的替换与更新.