深入解析 React 的 beginWork 流程

beginWork工作流程

面试题:beginWork 中主要做一些什么工作?整体的流程是怎样的?

Reconciler(协调器) 是 Render 阶段的第二阶段工作,整个工作的过程可以分为“递”和“归”:

  • 递:beginWork
  • 归:completeWork

beginWork 方法主要是根据传入的 FiberNode 创建下一级的 FiberNode。

整个 beginWork 方法的流程如下图所示:

首先在 beginWork 中,会判断当前的流程是 mount(初次渲染)还是update(更新),判断的依据就是 currentFiberNode 是否存在


csharp

代码解读

复制代码

if(current !== null){  // 说明 CurrentFiberNode 存在,应该是 update } else {  // 应该是 mount }

如果是 update,接下来会判断 wipFiberNode 是否能够复用,如果不能够复用,那么 update 和 mount 的流程大体上一致:

  • 根据 wip.tag 进行不同的分支处理
  • 根据 reconcile 算法生成下一级的 FiberNode(diff 算法)

无法复用的 update 流程和 mount 流程大体一致,主要区别在于是否会生成带副作用标记 flags 的 FiberNode

beginWork 方法的代码结构如下:


javascript

代码解读

复制代码

// 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:


javascript

代码解读

复制代码

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 会进入不同的方法:

  • mount:mountChildFibers
  • update:reconcileChildFibers

这两个方法实际上都是一个叫 ChildReconciler 方法的返回值:


javascript

代码解读

复制代码

var reconcileChildFibers = ChildReconciler(true); var mountChildFibers = ChildReconciler(false); ​ function ChildReconciler(shouldTrackSideEffects) {}

也就是说,在 ChildReconciler 方法内容,shouldTrackSideEffects 是一个布尔值

  • false:不追踪副作用,不做 flags 标记,因为你是 mount 阶段
  • true:要追踪副作用,做 flags 标记,因为是 update 阶段

在 ChildReconciler 方法内部,就会根据 shouldTrackSideEffects 做一些不同的处理:


javascript

代码解读

复制代码

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 算法生成下一级 FiberNode(diff 算法)

两个流程的区别在于“最终是否会为生成的子 FiberNode 标记副作用 flags”

在 beginWork 中,如果标记了副作用的 flags,那么主要与元素的位置相关,包括:

  • 标记 ChildDeletion,代表删除操作
  • 标记 Placement,代表插入或移动操作

原文:https://juejin.cn/post/7490506373899026486

<think>好的,用户想深入理解React源码,特别是架构解析。我需要根据提供的引用内容来组织回答。首先,用户可能对React的整体架构不太清楚,特别是Fiber架构。根据引用3,Fiber架构是为了解决性能瓶颈而引入的,所以应该先解释Fiber的基本概念和作用。然后,用户可能需要了解React的工作流程,比如任务调度和协调机制,引用2和5提到了任务调度循环和Fiber构造循环,以及时间分片,这部分需要详细说明。 接下来,用户可能对虚拟DOM和JSX的关系有疑问,引用1指出JSX是React.createElement的语法糖,生成虚拟DOM对象,这部分应该结合渲染流程来解释。然后,渲染的两个阶段,render和commit,引用4提到了commit阶段的工作,这里需要分阶段讲解,并提到effectList和Renderer的作用。 源码学习方法部分,用户可能需要步骤化的指导,比如从入口函数开始,调试工具的使用,核心模块如ReactDOM.render和Fiber节点的构造。另外,结合引用中的例子,比如双向链表(引用5)在时间分片中的应用,可以具体说明数据结构的重要性。 最后,常见问题部分应该覆盖用户可能遇到的难点,比如协调算法和事件系统,这些都是React的核心部分。参考文献的引用需要正确对应,比如引用1到5在适当的位置插入。 需要确保回答结构清晰,分点说明,语言通俗易懂,同时引用正确的内容来支持每个部分的解释。还要检查是否符合之前的格式要求,比如数学公式的格式,但这里可能涉及较少,主要关注代码块和引用标注。最后生成相关问题,帮助用户进一步学习。</think>### 如何深入理解 React 源码架构解析 #### 1. **核心架构:Fiber 与调度机制** React 的核心革新是 **Fiber 架构**(2017年引入),其目标是解决传统架构在高频更新和复杂组件树中的性能瓶颈[^3]。Fiber 的核心思想包括: - **时间分片(Time Slicing)**:将渲染任务拆分为多个小单元(Fiber节点),通过优先级调度避免长时间阻塞主线程。例如,每个节点计算后会校验是否超过5ms阈值,再决定是否让出主线程[^5]。 - **双向链表结构**:Fiber节点通过`child`、`sibling`、`return`指针形成链表,支持暂停和恢复渲染任务,完美契合增量更新需求。 - **任务调度循环**:与 **Fiber 构造循环** 协同工作,前者管理任务优先级,后者遍历组件树生成新的Fiber树[^2]。 #### 2. **渲染流程:Render 与 Commit 阶段** React 的渲染分为两阶段: - **Render 阶段**(协调阶段): - 生成 Fiber 树并计算差异(Diffing)。 - 通过`React.createElement`将 JSX 转换为虚拟 DOM 对象(引用[^1])。 - 生成 `effectList`(需更新的节点集合)。 - **Commit 阶段**(提交阶段): - 遍历 `effectList`,调用 **Renderer**(如`react-dom`)操作真实 DOM[^4]。 - 执行生命周期(如`componentDidUpdate`)。 #### 3. **关键源码模块解析** | 模块 | 功能 | |------------------|----------------------------------------------------------------------| | `ReactDOM.render`| 入口函数,初始化 Fiber 根节点并启动调度循环 | | `scheduleWork` | 调度任务优先级,决定何时执行 Fiber 构造 | | `beginWork` | 处理组件更新(如调用`render`方法)并生成子 Fiber 节点 | | `completeWork` | 收集副作用(如 DOM 操作),构建 `effectList` | #### 4. **源码学习方法** 1. **从入口函数切入**: - 通过`ReactDOM.render`或`useState`等 API 的调用链,跟踪 Fiber 树的构建过程。 - 示例代码片段: ```javascript // ReactDOM.js function render(element, container) { // 创建 FiberRoot 并启动调度 const root = createFiberRoot(container); scheduleUpdateOnFiber(root.current); } ``` 2. **调试工具辅助**: - 使用 Chrome DevTools 的 **React Profiler** 分析组件渲染时序。 - 在源码中插入`debugger`语句,观察 Fiber 节点的`alternate`(新旧节点指针)。 3. **核心数据结构**: - **Fiber 节点**:包含`tag`(组件类型)、`pendingProps`、`memoizedState`等字段。 - **UpdateQueue**:存储状态更新(如`setState`触发的变更队列)。 #### 5. **常见问题解析** - **协调算法(Reconciliation)**: 通过`Diffing`算法对比新旧 Fiber 树,复用 DOM 节点以减少性能损耗。例如,`key`的作用在此阶段体现。 - **事件系统**: React 实现 **合成事件(SyntheticEvent)**,通过事件委托统一管理,避免直接操作原生 DOM。 ####
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值