@ -3,36 +3,15 @@
在 React 中,视图的更新使用了双缓存的方式,也就是说在 React 运行时,同时有着两棵 Fiber 树,一颗是当前视图上的 Fiber 树,叫 `current` ,另外一棵是存在内存当中的下一次视图更新时用的叫做 `workInProgress` ,React 在构建时会创建整个 app 唯一的根 Fiber 节点,叫做 `FiberRootNode` ,这个节点上有一个 `current 指针` ,指向的是当前正在页面上显示的 Fiber 树也就是 `current` ,当 `workInProgress` 递归生成完毕,指针会立即指向 workInProgress ,而旧的 `current` 就会在下一次渲染中变成 `workInProgress` ,就这样循环交替完成页面的递归渲染
在 React 中,视图的更新使用了双缓存的方式,也就是说在 React 运行时,同时有着两棵 Fiber 树,一颗是当前视图上的 Fiber 树,叫 `current` ,另外一棵是存在内存当中的下一次视图更新时用的叫做 `workInProgress` ,React 在构建时会创建整个 app 唯一的根 Fiber 节点,叫做 `FiberRootNode` ,这个节点上有一个 `current 指针` ,指向的是当前正在页面上显示的 Fiber 树也就是 `current` ,当 `workInProgress` 递归生成完毕,指针会立即指向 workInProgress ,而旧的 `current` 就会在下一次渲染中变成 `workInProgress` ,就这样循环交替完成页面的递归渲染
本次源代码解读的 DEMO 只用了最简单的 `useState` 做计数器修改触发页面刷新,并没有新增新的 DOM 节点之外的其他操作,第一次和第二次和之后页面刷新实际上都只是 `useState` 触发变化并在页面上显示计数内容出现变化
本次源代码解读的 DEMO 只用了最简单的 `useState` 做计数器修改触发页面刷新,并没有新增新的 DOM 节点之外的其他操作,第一次和第二次和之后页面刷新实际上都只是 `useState` 触发变化并在页面上显示计数内容出现变化
# React 17.0.2
# React 17
## 首屏渲染
## 首屏渲染
React 的首屏渲染时会交由 createWorkInProgress 函数生成 WorkInProgress 的第一个 Fiber 节点,这个节点就是 FiberNode,所以我们从 createWorkInProgress 函数开始讲解 React 中 Fiber 创建的流程,这个函数的主要工作就是根据传入的 Fiber 节点的 alternate 属性是否存在判断复用 Fiber 节点还是创建新的 Fiber 节点,具体的函数深入解析可以看 [[React 的深入探索 - createWorkInProgress]]
React 的首屏渲染时会交由 createWorkInProgress 函数生成 WorkInProgress 的第一个 Fiber 节点,这个节点就是 FiberNode,所以我们从 createWorkInProgress 函数开始讲解 React 中 Fiber 创建的流程,这个函数的主要工作就是根据传入的 Fiber 节点的 alternate 属性是否存在判断复用 Fiber 节点还是创建新的 Fiber 节点,具体的函数深入解析可以看 [[React 的深入探索 - createWorkInProgress]]
### beginWork
第一次 createWorkInProgress 执行完毕之后,接下来就会交给 beginWork 开始正式的 Fiber 递归,在 React 中 Fiber 的创建使用递归实现的深度优先遍历算法,即
>beginWork 执行在递归节点的 Fiber 创建之前,主要是为传入的 Fiber 节点根据类型创建第一个子 Fiber 节点
首先会对 current 做空值判断,因为对于首屏渲染而言,当前传入的 current Fiber 节点是 FiberNode,所以是存在的,会进入 current !== null 的逻辑,然后会判断当前 Fiber 节点是否发生变化,然后赋值 `didReceiveUpdate` 做更新标识,判断的条件如下:
1. 新旧 props 是否相同
2. Context 是否发生变化
3. Fiber 节点 type 是否发生变化
在本次首屏渲染中,以上的条件都为否,会进入后续条件判断,在后续的条件判断中主要是为了应付一些特殊的场景,例如错误边界和 legacy 模式,可以看到官方留下的代码注释,在我们现在的场景下,最终会走到 didReceiveUpdate 为 false 的情况下,也就是当前 Fiber 并没有变化,因为在之前的 createWorkInProgress 中 WorkFiberProgress 被赋值上了 current Fiber 的同名属性,所以它们会是相同的两个 FIber 节点,在这里的代码可以看到:beginWork 中还有对优先值 lanes 的一些处理,在这里先不展开,后续再补充
最后会走入 switch 环节,这了主要根据传入的 WorkInProgress Fiber 节点的类型进入不同的处理逻辑,这也是 beginWork 最主要的任务:为当前 WorkInProgress Fiber 节点创建它的第一个 Fiber 子节点,其内部根据 Fiber 的 tag 不同有对应的:
- updateClassComponent 处理 Class Component
- updateHostComponent 处理 Host Component
- updateSuspenseComponent 处理 Suspense Component
本次的是首屏渲染,传入的 WorkInProgress Fiber 为 FiberNode,所以会进入 HostRoot 的 case,执行 updateHostRoot 方法
### updateHostRoot
updateHostRoot 函数中首先会执行 pushHostRootContext 方法,这个方法和 Context 相关,现在暂且不谈
在 updateHostRoot 逻辑一开始,它会将 current 和 WorkInProgress 传递给 cloneUpdateQueue 这个函数,这个函数会将 Fiber 中的 queue 属性进行 clone,除非它们已经是 clone 之后的属性,那么具体是怎么做的判断是否是 clone 过的属性呢?其实很简单,它只是使用了 === 三个等于判断属性的引用是否相同,也就是说这个函数实际上做的是清除引用,保证在之后对 WorkInProgress 的操作不会影响到 current Fiber 树,判断两个 Fiber 树的 updateQueue 属性的引用完全相同,那么就会创建一个新的对象并重复赋值达到清除应用的目的,最后赋值给 WorkInProgress Fiber 节点
之后,再把 WorkInProgress Fiber 节点交给 processUpdateQueue 函数,这个函数主要是处理更新队列,在这里先不展开,TODO
再往后是一些针对服务端渲染的一些处理逻辑,服务端渲染也不是这次讨论的目的,也先跳过
最终调用 reconcileChildren 为 FIber 创建一个子 Fiber 节点并返回
至此一个节点的 beginWork 流程就走完了,下一次会根据是否存在子 Fiber 节点判断是执行当前 WorkInProgress Fiber 节点的 completeWork ,还是继续对子节点执行 beginWork
beginWork 执行在递归节点的 Fiber 创建之前,主要是为传入的 Fiber 节点根据类型创建第一个子 Fiber 节点
### completeWork
### completeWork