初始化渲染
- 首次渲染是从无FIber树到构建Current树并渲染到页面的过程,流程如下
graph LR
A[调用ReactDOM.createRoot/渲染根组件]-->B[创建根Fiber节点]
B-->C[Scheduler标记高优先级任务]
C-->D[启动协调阶段 构建WorkInProgress树]
D-->E[遍历组件树创建Fiber节点]
E-->F[创建真实DOM节点]
F-->G[标记Placement副作用]
G-->H[WorkInProgress树构建完成]
H-->I[进入提交阶段]
I-->J[执行DOM插入操作]
J-->K[切换RootFiber指针]
K-->L[页面首次渲染完成]
1. 根节点初始化:
- 调用
ReactDom.createRoot(rootDom).render(<App/>)时,React先创建Fiber节点(RootFiber),作为整个Fiber树的入口 - 根Fiber节点标记首次渲染标识,Scheduler为其分配最高优先级(无需等待,立即执行)
2. 协调阶段(构建WorkInProgress树)
- react-reconciler从RootFiber开始,深度优先遍历组件树(基于Fiber链表的child/return/sibling指针)
- 对每个组件
- 函数组件:执行组件函数,解析JSX生成虚拟DOM,在创建对应的Fiber节点
- 类组件:实例化组件(new ClassComponent()),调用render()方法生成虚拟DOM,创建Fiber节点
- 原生标签(如div):调用ReactDOM的createInstance创建真是DOM节点,关联到Fiber节点的stateNode属性
- 为所有Fiber节点标记Placement(插入)副作用(首次渲染所有节点都需要插入DOM)
3. 提交阶段(渲染到页面)
- 遍历副作用链表,调用ReactDOM的appendChild将真实DOM节点插入到根容器
- 执行组件生命周期/钩子:函数组件执行useEffect(无cleanup)、类组件执行componentDidMount
- 切换RootFiber的current指针,将WorkInProgress树设为Current树,首次渲染完成
更新渲染(状态/props变化)
- 更新渲染时React最核心的场景(如setState/useState触发),完整流程覆盖(更新触发-->调度-->协调-->提交)全链路,也是所有核心模块协作的核心体现
graph TD
A[触发更新setStte/useState/Props变化] --> B[创建 Update 对象 加入Fiber节点的更新队列]
B --> C[标记 Fiber节点为待更新 请求Scheduler调度]
C --> D[Scheduler 分配优先级expirationTime 加入任务队列]
D --> E[Scheduler 启动调度循环-检查切片高优先级任务]
E -- 有执行时间 --> F[react-reconciler启动协调阶段 复用Current树构建 WorkInProgress 树]
E -- 无执行时间 --> G[暂停任务 让出主线程 等待下一次调度]
F --> H[计算组件最新状态 消费Update队列]
H --> I[对比新旧Fiber节点 Diff算法]
I --> J[标记差异对应的副作用 Placement Update Deletion]
J --> K{是否有下一个Fiber节点?}
K -- 是 --> E
K -- 否 --> L[WorkInProgress 树构建完成]
L --> M[react-reconciler进入提交阶段]
M --> N[执行副作用 先删Deletion--> 再插 更 Placement Update]
N --> O[执行生命周期钩子 useEffect清理+执行 componentDidUpdate]
O --> P[切换 RootFiber指针 WorkInProgress --> Current]
P --> Q[页面更新完成]
G --> E
1. 更新触发与队列管理
- 当调用setState(newState)或setCount(c=> c+1)时,React不会立即更新状态,而是创建Update对象(包含新状态、优先级、回调),加入该组件Fiber节点的更新队列(链表结构)
- 若同一事件循环内多次调用setState,Update会批量接入队列(批量更新优化),避免多次渲染
2. 任务调度(Scheduler核心副作用)
- Fiber节点标记待更新后,向Scheduler发起调度请求
- Scheduler为任务分配过期时间(expiration Time)(优先级越高,过期时间越短),加入优先级队列
- Scheduler启动调度循环
- 先检查是否有过期任务(必须立即执行)
- 再检查主线程空闲时间,分配时间切片(<=5ms)
- 若有高优先级任务(如用户输入),中断当前低优先级任务,优先执行高优先级任务
3. 协调阶段(react-reconciler + Fiber核心)
- react-reconciler拿到执行权限后,从RootFiber开始,基于Current树复用已有Fiber节点创建的WorkInProgress树(避免重复创建节点)
- 消费更新队列:遍历组件的Update链表,计算组件最新状态(如类组件合并setState的多个Update,函数组件并执行useState的更新函数)
- Diff算法执行
- 同层级对比:值对比同一层级的Fiber节点,跨层级直接标记删除 + 插入
- 组件类型对比:类型不同则销毁就Fiber树,类型相同则对比Props
- 列表对比:通过Key匹配节点,用最长递增子序列最小化DOM移动
- 标记副作用:为有差异的节点标记effectTag(如Update:更新属性、Deletion:删除节点),并将所有副作用节点串联成副作用链表(提升提交阶段效率)
4. 提交阶段(不可中断,保证DOM一致性)
- 副作用执行:按先删后插的顺序遍历副作用链表,避免DOM布局偏移
- Deletion:删除不需要的DOM节点,执行类组件componentWillUnmount、函数组件useEffect的清理函数
- Placement/Update:插入新DOM节点、更新已有节点的属性(如className、事件监听)
- 生命周期/执行钩子:类组件执行componentDidUpdate,函数组件执行useEffect(先执行上一轮的cleanup,再执行本轮的回调)
- 切换Fiber树:将RootFiber的Current指针指向WorkInProgress树,完成双缓存的切换,更新流程结束
React18+新增:并发渲染(Concurrent Rendering)
React18 基于上述核心流程,新增并发渲染能力,核心是让协调阶段支持暂停/恢复/放弃,主要体现在:
1. 优先级调度强化: 通过startTransition/useDeferredValue标记低优先级更新,Scheduler会优先处理高优先级任务(如输入),低优先级任务可被中断
2.Suspense支持: 渲染懒加载组件时,react-reconciler会暂停协调阶段,直到组件加载完成,再恢复渲染(fallback内容临时展示)
3.自动批处理: 无论更新新触发位置(如setTimeout/事件回调),都默认批量处理Update,减少渲染次数
核心总结
1. 两大阶段
- 协调阶段(可中断,计算差异标记副作用) + 提交阶段(不可中断,执行DOM操作)
2. 双缓存树:
- Current树对应真实的DOM,WorkInProgress树计算差异,切换指针完成更新
多模块协作
3.1 依赖底层:Fiber架构是基础载体
- Fiber节点是最小任务单元:每个Fiber节点对应一个组件/原生节点,存储了节点类型、Props、优先级、副作用、遍历指针(return/child/sibling)等核心信息
- Fiber树是状态容器:维护Current(当前渲染树)和WorkInProgress(构建中树)双缓存结构,是react-reconciler执行Diff的基础
- 对其他模块的支撑
- 给Scheduler:提供任务中断点--Fiber链表结构可保存遍历状态,让Scheduler能暂停/恢复任务
- 给react-reconciler:提供遍历对象--react-reconciler遍历Fiber树完成Diff和副作用标记
3.2 调度核心:Scheduler调度器是大脑、任务管理者
- Scheduler不直接处理渲染逻辑,只负责任务的调度规则,依赖Fiber的优先级信息,为react-reconciler提供执行时机
- 依赖Fiber:读取Fiber节点的expirationTime(过期时间)确定任务优先级,决定是否中断当前react-reconciler
- 支撑react-reconciler
- 为react-reconciler的协调任务分配时间切片(<=5ms),避免阻塞主线程
- 当高优先级任务(如用户输入)到来时,中断当前低优先级的协调任务,让react-reconciler先处理高优先级任务
3.3 执行核心:react-reconciler协调层是业务执行者
- 负责具体执行什么任务(基于Fiber节点执行Diff算法、标记副作用、驱动平台更新)
- react-reconciler是连接Scheduler、Fiber和具体渲染平台的桥梁,同时依赖前两者
- 依赖Scheduler:遵循Scheduler的调度指令--只在Scheduler分配的时间片内执行协调任务,收到中断信号时立即暂停
- 依赖Fiber:遍历Fiber树执行Diff算大,标记副作用(effectTag),并将副作用链表传递给平台层
- 对外输出:将协调结果(副作用)转化为平台特定操作(如DOM插入/更新),实现跨平台渲染
- 更新队列、合成事件系统、Render等模块补充触发更新和平台落地能力