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 | performSyncWorkOnRoot.bind(null, root); // 同步的回调函数 |
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阶段完成后再异步执行。
