2024-08-27 10:10:05 +08:00

152 lines
4.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# beginWork工作流程
> 面试题beginWork 中主要做一些什么工作?整体的流程是怎样的?
Reconciler协调器 是 Render 阶段的第二阶段工作,整个工作的过程可以分为“递”和“归”:
-beginWork
-completeWork
![image-20230224111517826](https://xiejie-typora.oss-cn-chengdu.aliyuncs.com/2023-03-10-053722.png)
beginWork 方法主要是根据传入的 FiberNode 创建下一级的 FiberNode。
整个 beginWork 方法的流程如下图所示:
<img src="https://xiejie-typora.oss-cn-chengdu.aliyuncs.com/2023-03-01-015305.png" alt="image-20230301095305141" style="zoom:50%;" />
首先在 beginWork 中,会判断当前的流程是 mount初次渲染还是update更新判断的依据就是 currentFiberNode 是否存在
```js
if(current !== null){
// 说明 CurrentFiberNode 存在,应该是 update
} else {
// 应该是 mount
}
```
如果是 update接下来会判断 wipFiberNode 是否能够复用,如果不能够复用,那么 update 和 mount 的流程大体上一致:
- 根据 wip.tag 进行不同的分支处理
- 根据 reconcile 算法生成下一级的 FiberNodediff 算法)
无法复用的 update 流程和 mount 流程大体一致,主要区别在于是否会生成带副作用标记 flags 的 FiberNode
beginWork 方法的代码结构如下:
```js
// current 代表的是 currentFiberNode
// workInProgress 代表的是 workInProgressFiberNode后面我会简称为 wip FiberNode
function beginWork(current, workInProgress, renderLanes) {
// ...
if(current !== null) {
// 进入此分支,说明是更新
} else {
// 说明是首次渲染
}
// ...
// 根据不同的 tag进入不同的处理逻辑
switch (workInProgress.tag) {
case IndeterminateComponent: {
// ...
}
case FunctionComponent : {
// ...
}
case ClassComponent : {
// ...
}
}
}
```
关于 tag在 React 源码中定义了 28 种 tag
```js
export const FunctionComponent = 0;
export const ClassComponent = 1;
export const IndeterminateComponent = 2; // Before we know whether it is function or class
export const HostRoot = 3; // Root of a host tree. Could be nested inside another node.
export const HostPortal = 4; // A subtree. Could be an entry point to a different renderer.
export const HostComponent = 5;
export const HostText = 6;
export const Fragment = 7;
// ...
```
不同的 FiberNode会有不同的 tag
- HostComponent 代表的就是原生组件div、span、p
- FC 在 mount 的时候,对应的 tag 为 IndeterminateComponent在 update 的时候就会进入 FunctionComponent
- HostText 表示的是文本元素
根据不同的 tag 处理完 FiberNode 之后根据是mount 还是 update 会进入不同的方法:
- mountmountChildFibers
- updatereconcileChildFibers
这两个方法实际上都是一个叫 ChildReconciler 方法的返回值:
```js
var reconcileChildFibers = ChildReconciler(true);
var mountChildFibers = ChildReconciler(false);
function ChildReconciler(shouldTrackSideEffects) {}
```
也就是说,在 ChildReconciler 方法内容shouldTrackSideEffects 是一个布尔值
- false不追踪副作用不做 flags 标记,因为你是 mount 阶段
- true要追踪副作用做 flags 标记,因为是 update 阶段
在 ChildReconciler 方法内部,就会根据 shouldTrackSideEffects 做一些不同的处理:
```js
function placeChild(newFiber, lastPlacedIndex, newIndex){
newFiber.index = newIndex;
if(!shouldTrackSideEffects){
// 说明是初始化
// 说明不需要标记 Placement
newFiber.flags |= Forked;
return lastPlacedIndex
}
// ...
// 说明是更新
// 标记为 Placement
newFiber.flags |= Placement;
}
```
可以看到,在 beginWork 方法内部,也会做一些 flags 标记(主要是在 update 阶段),这些 flags 标记主要和元素的位置有关系:
- 标记 ChildDeletion这个是代表删除操作
- 标记 Placement这是代表插入或者移动操作
## 真题解答
> 题目beginWork 中主要做一些什么工作?整体的流程是怎样的?
>
> 参考答案:
>
> 在 beginWork 会根据是 mount 还是 update 有着不一样的流程。
>
> 如果当前的流程是 update则 WorkInProgressFiberNode 存在对应的 CurrentFiberNode接下来就判断是否能够复用。
>
> 如果无法复用 CurrentFiberNode那么 mount 和 update 的流程大体上是一致的:
>
> - 根据 wip.tag 进入“不同类型元素的处理分支”
> - 使用 reconcile 算法生成下一级 FiberNodediff 算法)
>
> 两个流程的区别在于“最终是否会为生成的子 FiberNode 标记副作用 flags”
>
> 在 beginWork 中,如果标记了副作用的 flags那么主要与元素的位置相关包括
>
> - 标记 ChildDeletion代表删除操作
> - 标记 Placement代表插入或移动操作