react更新流程

我们了解了 react 的Scheduler-Reconciler-Renderer架构体系

  • Scheduler负责任务的优先级调度
  • Reconciler工作的工作阶段被称为render阶段。因为在该阶段会调用render方法
  • Renderer工作的阶段被称为commit阶段。commit阶段会把render阶段提交的信息渲染到页面上。

前面已经介绍了render阶段commit阶段render阶段完成后会进入commit阶段,而render阶段之前就是触发状态更新阶段

render 阶段前阶段

状态更新的整个调用路径的关键节点:

触发状态更新

在 react 中,有以下方法可以触发状态更新:

  • this.setState
  • this.focusUpdate
  • ReactDOM.render
  • useState
  • useReducer

每次触发状态更新都会走一遍render阶段前阶段->render阶段->commit阶段这个流程。

创建 Update 对象

react中,有多种触发状态更新的方法,他们是如何保持同一套状态更新机制呢?
每次状态更新都会创建一个保存更新状态相关内容的对象,我们叫他Update。在render阶段会根据Update计算新的state

从 fiber 到 root,得到 rootFiber

这个阶段,触发状态更新的fiber上已经包含了Update对象。
我们知道,render阶段是从rootFiber开始向下遍历。那么如何从触发状态更新的fiber得到rootFiber呢?
答案是:调用markUpdateLaneFromFiberToRoot
这个方法的工作:从触发状态更新的fiber一直向上遍历到rootFiber,得到rootFiber,并返回rootFiber

调度更新

现在我们拥有了一个rootFiber,该rootFiber对应的fiber树中的某个fiber节点包含一个Update。接下来通知Scheduler根据更新的优先级,决定以同步还是异步的方式调度本次更新。
调用的方法是ensureRootIsScheduled
这个方法会根据优先级调度回调函数执行,这里调度的回调函数为:

1
2
performSyncWorkOnRoot.bind(null, root); // 同步的回调函数
performConcurrentWorkOnRoot.bind(null, root); // 异步的回调函数

这个回调函数也是render阶段的入口函数。

render 阶段

同步或异步调度本次更新,根据 rootFiber 得到 fiber 树

commit 阶段

副作用对应的DOM操作在 commit 阶段执行。
执行 DOM 操作前:

  • 处理DOM节点渲染后的autoFocus/blur逻辑
  • 会调用getSnapshotBeforeUpdate,能在操作 DOM 前捕获 DOM 信息(如滚动位置)
  • 异步调度useEffect

执行 DOM 操作:

  • 通过一次插入 DOM 操作将整颗DOM树插入页面。
  • 执行useEffect、useLayoutEffect销毁函数
  • 会调用componentWillUnmount
  • 解绑 ref

执行 DOM 操作后:

  • 会调用useLayoutEffect回调函数
  • 调度useEffect,在Layout阶段完成后再异步执行useEffect回调函数
  • 调用this.setState的第二个参数回调函数

useLayoutEffect从上一次更新的销毁函数调用到本次更新的回调函数调用是同步执行的。
useEffect则需要先调用,在Layout阶段完成后再异步执行。

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×