一次完整的渲染流程
- JSX代码通过React.createElement方法生成ReactElement结构
- ReactElement结构是树结构,由根节点向子节点扩展
- 节点之间根据child连接
- Element结构通过schedule的调度reconcile去生成fiber结构树
- fiber结构是链表结构,由fiber根节点(HostRootFiber)开始延展
- 节点之间根据child、return、sibling的关系组成链表
- React18正式推出了并发更新,是基于fiber的链表结构实现
- 🚧注意:虽然ReactV16就提出了fiber,但是并没有默认使用并发特性
- fiber结构树通过commit操作生成真实DOM元素
- commit分为三个阶段before mutation、mutation、layout
- before mutation在DOM操作之前
- mutation阶段在增删DOM节点
- layout阶段在DOM操作之后

render阶段
举个🌰
export default function App() {
const [count,setCount] = useState(0)
const handleClick=()=>{
setCount(count+1)
}
return(
<div>
hello akechi,
<p> 这是我的count,值为{count} </p>
<button onClick={handleClick} >按钮</button>
</div>
)
}
🚧前置概念:
- 双缓冲树:React的fiber树有两颗
- 在渲染层展示,根节点fiberRoot的current指向它,所以它也称为current
- 在内存中构建的fiber树是workInProgressFiber
- 当构建的workInProgressFiber完成后,React通过current指向将内存中的fiber树挂载,此时这棵树变成了current。当React需要更新时,会根据current复用没有改变的fiber节点,从而加速fiber树的构造速度
在构建fiber树的过程中:
- 初始化
- 创建一个fiberRoot作为整个应用的根节点,需要注意的是整个应用只有一个fiberRoot

- 创建HostRootFiber,fiberRoot的current指向HostRootFiber,一个应用可以有多个HostRootFiber
- 复用当前current树的alternate作为workInProgressFiber,如果没有alternate就创建

- 创建一个fiberRoot作为整个应用的根节点,需要注意的是整个应用只有一个fiberRoot
- workLoop循环阶段:
- fiber树的构造是深度优先遍历
- wip!==null时,循环调用performUnitOfWork方法
- 通过beginWork方法向下调和找到next节点,当next无sibling节点(兄弟节点)调用,completeWork方法向上归并
- workInProgress指针逐个构造fiber节点

- 构造完成
- workInProgress置为nul
- workInProgressFiber构建完成,fiberRoot的current指向它

在更新fiber树节点过程中:
- 以渲染层的fiber树为基础,复用fiber节点,并修改更新的fiber节点
- fiberRoot更改current指向,切换workinProgressFiber为渲染层的fiber树

render的中断渲染在哪里?
首先看下调度中心示意图

React根据scheduler包实现了任务调度,然后workLoop中使用停顿机制,具体可以参考这篇文章React的调度原理
function workLoopConcurrent() {
//shouldYield开启实现停顿
while (workInProgress !== null && !shouldYield()) {
performUnitOfWork(workInProgress);
}
}
React的中断渲染的原理:当监测到执行任务队列中的task超时,shouldYield为true,退出循环把线程交还给浏览器进行更重要的工作(如:绘制UI防止界面卡顿),等浏览器空闲时调用requestHostCallback发送MessgaeChannel重新循环处理task队列
commit阶段的三个阶段
- before muation:处于执行DOM操作之前,比如获取DOM节点的快照,异步调取useEffect
- Muation :处于DOM渲染的过程,执行真正的DOM操作
- layout:处于DOM更新完成后的过程,比如执行setState的回调,或者触发useLayoutEffect等钩子
🚧🚧提问:为什么React要设计useEffect、useLayoutEffect两个钩子呢?
如果你在useEffct的初始化渲染中修改了展示的数据或者css样式,那么很有可能会重复渲染导致闪屏用户体验不好的问题,此时可以试下useLayoutEffect这个钩子