在render阶段,发生更新的组件,会被打上effect tag。这些组件在render阶段的“归”阶段,会形成链表,这条链表在commit会被遍历,并执行响应的操作,在commit阶段,这个操作叫做mutation。
对于dom节点,mutation代表dom节点的增删改,整个commit阶段可以分成3个子阶段,分别是mutation 前(before mutation阶段)、mutation阶段、mutation后(layout阶段)。

commitRoot-commit开始阶段的方法

image.png
执行runWithPriority(由调度器提供),这个函数的第一个参数是调度的优先级,第二个参数是调度的回调函数。

commitRootImpl

image.png
1、判断rootWithPendingPassiveEffects是否为null,执行flushPassiveEffects()。表示开始本次commit之前,是否有还未执行的effect,如果有,则执行他们。
对于HostComponent,如果需要插入到dom,对象的dom节点的effect tag为Placement。
对于FunctionComponent来说,如果存在需要调用的useEffect,那么这个FunctionComponent的effect tag会增加passiveEffect。
2、处理离散事件:用户点击的事件,在离散事件中,react要处理光标的focus和blur等状态(所以需要单独处理)-了解。
image.png
3、重置render阶段使用的全局变量。
image.png
4、处理包含effect tag的链表
在render阶段,已经形成了包含effect tag的fiber 链表,为哈此次还有?
在归阶段的update时,这条链表只会挂载到自己的子Fiber,当前应用的根节点,是没有被挂载在这条链表上的
image.png
如果finishedWork的effectTag存在,如果当前应用的根节点的effectTag存在,需要将当前应用的根节点挂载在这条包含effectTag的最后。如果当前应用的根节点不存在effectTag时,第一个要处理的就是当前根节点的firstEffect。
4.1 当firstEffect存在时,此处循环,执行commitBeforeMutationEffcts()。这个循环代表commit BeforeMutation执行的工作。
image.png
5、再一次循环,执行commitMutationEffects,代表commit Mutation的工作。
image.png
6、再一次循环,执行commitLayoutEffects,代表执行的是layout的工作。
image.png
7、如果rootDidHavePassiveEffects存在,会将root的值赋值给rootDidHavePassiveEffects。(与本次更新的useEffect执行有关)。当rootDidHavePassiveEffects不存在useEffect的调用,会遍历包含effectTag的链表,将响应的变量赋值为null,方便垃圾回收。
image.png
8、是否是无线循环的更新,实则抛出错误。
image.png
9、进到commit阶段的结尾时,由于commit阶段可能产生新的更新,所以在commit结尾将整个应用根节点重新调度。
image.png
10、将同步的更新放在flushSyncCallback的队列中。每一次执行flushSyncCallback,就会执行队列中的同步任务。
例如在functionComponent来说,在useLayoutEffect中,触发更新,触发的更新就是一个同步的更新
image.png

  1. import "./App.css"
  2. import { useLayoutEffect, useState } from "react"
  3. function App() {
  4. const [num, setNum] = useState(0)
  5. useLayoutEffect(() => {
  6. if (num === 2) {
  7. setNum(num + " layout")
  8. }
  9. }, [num])
  10. const handleNum = () => {
  11. let _num = num + 1
  12. setNum(_num)
  13. }
  14. return (
  15. <div className="App">
  16. <header className="App-header">
  17. <p onClick={handleNum}>
  18. <code title={num}>{num}</code>
  19. </p>
  20. <a className="App-link" href="https://reactjs.org" target="_blank" rel="noopener noreferrer">
  21. Learn React
  22. </a>
  23. </header>
  24. </div>
  25. )
  26. }
  27. export default App

image.png
两处断点,在flushSyncCallback中执行同步的更新,界面变成2 layout。但是实际中,却没有。
image.png
原因:

  1. import React from "react"
  2. import ReactDOM from "react-dom/client"
  3. import App from "./App"
  4. ReactDOM.render(
  5. <React.StrictMode>
  6. <App />
  7. </React.StrictMode>,
  8. document.getElementById("root")
  9. )

image.png

  1. import React from "react"
  2. import ReactDOM from "react-dom/client"
  3. import App from "./App"
  4. const root = ReactDOM.createRoot(document.getElementById("root"))
  5. root.render(
  6. <React.StrictMode>
  7. <App />
  8. </React.StrictMode>
  9. )

因为目前使用的ReactDOM.render的legacy模式应用,这个模式下所有的更新都是同步执行,不存在优先级的关系。concurrent模式支持并发模式的执行。执行到前一个断点界面变成2,后一个断点变成2 layout。