React系列 - setState 更新流程

741 阅读3分钟

React系列 - setState 更新流程

渲染流程

react 通过 jsx 来描述界面,jsx可以通过 babel等编译器来编译成 renderFunction,然后执行后产生虚拟dom;

vdom也不是直接渲染的,而是先转化为fiber,之后再渲染

  • 在 react15 中,vdom是一个树结构,每个节点只记录了 子节点(children) , 没有记录兄弟节点,所以必须一次性渲染完,不能中断。
  • 而 转成 filber 的链表结构就会记录 父节点(return) , 子节点(children) , 兄弟节点 (sibing) , 就变成了可中断的。

从 vdom 转换成 fiber 的过程就叫做 reconcile,转换过程中会顺便创建对应的 dom 元素,然后全部转换完成后一次性 commit 到 dom。

这个过程不是一次性的,是通过 scheduler 调度执行的,也就是可以分批次执行,这就是可打断的含义。

Fiber 架构下的渲染流程

react 把 schedulereconcile 叫做 render 阶段,这个阶段就是把 vdom 转换为 fiber

schedule 只是让 reconcile 可以分多次执行,可以打断,但做的事情是不变的,所以 schedule 也是 render 阶段的一部分

之后把 filber 更新到 dom 的过程就叫做 commit 阶段。

整个渲染流程的入口就是 performSyncWorkOnRoot 函数。

render 阶段会执行一个调度的 loop -> workLoopSync() , 这个 loop 就是不断处理一个个filber 的 reconcile

每个节点都有 beginWorkcompleteWork 两个阶段,因为要做 vdomfibervdom 是一个树形结构,需要递归处理

具体的 reconcile 逻辑不同:

  • 比如函数组件会被调用,拿到 render 出的 vdom 继续进行 reconcile
  • 比如class组件会创建实例,调用 render 方法,拿到 vdom , 然后再继续 reconcileChildren

总之,vdom 转 fiber 是一个递归进行的过程,最后再进行 commit 阶段

setState的流程

渲染的入口是 preformSyncWorkOnRoot 函数,setState修改完状态后,触发这个函数即可。

setState 会调用 dispatchAction , 创建一个 update 对象放到 fiber 节点的 updateQueue 上,然后调度渲染。

react 会先触发 updatefiber 往上找到 根fiber 节点,然后再调用 performSyncWorkOnRoot 的函数进行渲染

而 setState 是同步还是异步,也就是在这一段控制的

scheduleUpdateOnFilber更新函数中,有个判断条件里有个 excutionContext , 这个是用来标识当前环境的,比如是批量还是非批量,是否执行过 render 阶段,commit 阶段

ReactDom.render 执行的时候会先调用 unBatchUpdate 函数,这个函数会在 excutionContext 中设置一个 unbatch 的 flag, 这样在 update 的时候,就会立刻执行 preformSyncWorkOnRoot来渲染,因为首次渲染的时候是要马上渲染的,没必要调度。

之后走到 commit 阶段会设置一个 commit 的 flag

然后再次 setState 就不会走到 unbatch 的分支了。

为什么 setTimeout 里面的 setState 会 同步执行呢?

因为直接从 setTimeout 执行的异步代码是没有设置 excutionContext 的, 那就会走到 NoContext 的分支,会立刻渲染。

其实按理来说,setState不能叫异步,还是在同一个调用栈进行的,只不过顺序不同而已。

只能叫批量还是非批量。