相恨不如潮有信,相思始觉海非深。
React 入口函数模式
- legacy mode 同步阻塞模式
1 | ReactDOM.render(<App />, document.getElementById("root")); |
- blocking 模式
1 | ReactDOM.createBlockingRoot(document.getElementById("root")) |
- concurrent mode 并行模式
1 | ReactDOM.createRoot(rootNode).render(<App />); |
- 区别: legacy模式和concurrenrt模式在于协调过程是否可中断
React 入口函数特性对比
Legacy 模式
- Legacy 模式下,react 更新
1 | import ReactDOM from "react-dom"; |
- 如果 fiber tree 的结构很复杂,那么协调 fiber tree 可能会占用大量的时间,导致主线程会一直被 js 引擎占用,渲染引擎无法在规定时间(浏览器刷新频率 - 16.7ms)内完成工作,使得页面出现卡顿(掉帧),影响用户体验。
Concurrent 模式
- Concurrent 模式下,react 更新
1 | import ReactDOM from "react-dom"; |
- Concurrent 模式下的协调过程,js 引擎不会一直占用主线程,到了规定时间就会自动让出主线程。
- 具体来说,整个协调过程是分段进行的,每个时间段为 5ms。如果在规定的 5ms 内,协调过程没有结束,js 引擎会自动让出主线程。此时由于协调还没有结束,react 会请求浏览器分配的一个 5ms 的时间片继续进行协调过程。整个过程会一直重复,直到协调结束为止。
- 在Concurrent 模式下主线程会定时被 js 引擎释放,相比 legacy 模式,在一定程度上能提升用户体验。
思考
- 不管是 Legacy 模式,还是 Concurrent 模式,我们都是使用 createRoot 这个 API 来启动 react 应用,并没有使用我们非常熟悉的 render 方法。
这例子唯一的区别是在 click 事件回调方法中直接调用了 setState,而 concurrent模式是在通过 useTransition 来调用 setState。直接调用 setState,react 采用 Legacy 模式来协调 fiber tree;通过 useTransition 调用 setState,react 采用 Concurrent 模式来协调 fiber tree。
之所以会这样,是因为 React18 将不再支持 render(不推荐使用),我们需要通过 createRoot 来启动一个 react 应用。应用启动以后,react 会根据触发更新的上下文来决定采用何种模式来协调 fiber tree。如果是在 event、setTimeout、network request 的 callback 中触发更新,react 会采用 Legacy 模式,而如果更新与 Suspense、useTransition、OffScreen 相关,那么 react 会采用 Concurrent 模式。
Concurrent 模式的意义
- Concurrent 模式最大的意义,就是借着可中断的协调,来提升应用的用户体验。
- 协调不会长时间阻塞浏览器渲染
- 高优先级更新可以中断低优先级更新,优先渲染
- 通过 Suspense、SuspenseList、useTransition、useDeferredValue 这些新的 API,给用户提供更好的交互体验