架构
-
接口层
react
-
内核层
- 调度器
scheduler
,负责执行回调 - 构造器
react-reconciler
,调度配合react, react-dom,scheduler
- 渲染器
react-dom
,生成DOM节点或SSR字符串
- 调度器
工作循环
-
任务调度循环,在
scheduler
中采用堆排序算法清空任务队列,执行宏观的任务(task)
-
fiber构造循环,在
react-conciler
的ReactFiberWorkLoop
中,是任务调度循环的一部分采用深度遍历算法,是任务(task)的一部分,比如task包含:
fiber
树的构造,DOM渲染,调度检查
主干逻辑
- 输入:将每次更新视为一次
更新需求
- 注册:
react-reconciler
收到更新需求
,不会立即更新Fiber树,而是在scheduler
注册一个任务task
,将更新需求
转换成task
- 执行调度任务(输出):
scheduler
通过任务调度循环
执行task
,task执行会重新返回到react-reconciler
中fiber构造循环
:构建出最新的FibercommitRoot
:将最新的fiber
树渲染到页面上
任务调度循环
和fiber构造循环
可实现批量更新,可中断渲染
高频对象
- react
ReactElement
: React元素,可能是HTML原生,可能是组件ReactComponent
:class类型的React元素,会在reconciler阶段生成fiber对象,触发对应的生命周期,调用render方法获取jsx函数式组件
:reconciler阶段执行获取子节点
- react-reconciler
- fiber对象:保存了节点类型,副作用,父子兄fiber信息
- UpdateQueue对象:链式队列,有WIP和Current两个队列。更新会按顺序,渲染会跳过低优先级
- Hook对象:hooks背后的对象,是一个链式队列
- Scheduler
- Task对象:小顶堆数组,每个元素是一个Task
启动
-
创建
ReactDomRoot
对象暴露
render,unmount
方法,引导React启动 -
创建
fiberRoot
对象,包含fiber树的全局上下文,fiber构造循环
的各种状态,控制执行逻辑 -
创建
HostRootFiber
对象,第一个Fiber对象,fiber树的根节点
最终调用updateContainer
,进入react-reconciler
,调用schedulerUpdateOnFiber

reconciler运作原理
- reconciler的主要作用是:
- 暴露api,如给react-dom暴露
schedulerUpdateOnFiber
- 注册调度任务:在
react-scheduler
上注册任务 - 执行任务回调:构建
fiber
树,与react-dom交互构造与fiber
树对应的DOM
节点 - 输出:与react-dom交互,渲染DOM节点
- 暴露api,如给react-dom暴露
- 注册调度任务:逻辑在
ReactFiberWorkLoop
中-
会根据任务的优先级
performSyncWorkOnRoot
直接构造fiber
树,还是ensureRootIsScheduled
注册调度任务 -
ensureRootIsScheduled
首先会判断是否需要注册新的调度然后注册任务,在任务中添加回调,会根据优先级类型使用同步的
performSyncWorkOnRoot
还是异步的performConcurrentWorkOnRoot
回调构造fiber
树
-
- 执行任务回调:
-
同步
performSyncWorkOnRoot
构造
fiber
树判断异常
提交输出
commitRoot
-
异步
performConcuurrentWorkOnRoot
检查是否正在
render
,是否需要恢复上一次渲染如果渲染被中断,返回新的
performConcuurrentWorkOnRoot
等待下一次调用,这就是可中断
-
- 输出:主要执行不同生命周期的副作用队列
commitBeforeMutationEffects
:DOM突变前的副作用队列commitMutationEffects
:DOM变更的副作用队列commitlayoutEffects
:DOM变更后的副作用队列
优先级管理
- fiber树的的
lanePriority
,采用位运算,位数越低优先级越高 - scheduler的
schedulerPriority
- 用于上面两套系统的转换
ReactPriorityLevel
调度管理
-
核心函数
requestHostCallback
:请求及时调度 MessageChannelcancelHostCallback
:取消及时调度 scheduledHostCallabck = nulrequestHostTimeout
:请求延时调度 setTimeoutcancelHostTimeout
:取消延时调度 cancelTimeoutshouldYieldToHost
:是否让让出主线程 current ≥ deadline && (needPaint || inputPending)requestPaint
:请求请求绘制 needPaint = truegetCurrentTime
:获取现在时间 performance.now()forceFrameRate
:强制设置让出主线程间隔 yieldInterval = 1000 / fps
-
调度中心
调度中心通过宏任务
MessageChannel
异步地请求和消费调度,将任务回调保存在scheduledHostCallabck
中,在MessageChannel.port
的onmessage中判断callback是否被取消requestHostCallback
请求调度performWorkUntilDeadline
消费调度hasMoreWork
有更多任务,再度请求调度

-
任务
任务创建会根据优先级设置一个过期时间
expirationTime
,优先级越高,过期时间越短,任务插入任务队列的sortIndex
就是expirationTime
-
消费任务
- 调用
requestHostCallback
消费任务,flushWork
是回调函数,内部调用workLoop
循环消费任务队列。 workLoop
会不断从taskQueue
取出任务,调用currentTask.callback
(callback可中断)消费,callback
的执行在react-reconciler
中构建fiber
树,callback可能产生新的回调,重新保存在currentTask.callback
。- 每次取出前会判断是否超时
currnetTask.expriationTime > currentTime
,应该交还给主线程shouldYieldToHost
,还有剩余时间hasTimeRemaining
时间切片:消费每个任务前都会进行超时检查
可中断渲染:每个
task.callback
可以自己检测超时,fiber构造过程中,每构造完一个单元performUnitOfWork
都会进行超时检查shouldYield
,超时就退出fiber树构造循环
- 调用
-
注册任务时的节流防抖
节流:
exsitingCallbackPriority === newCallbackPriority
优先级相同,沿用上一个task,无需注册新的task防抖:
existingCallbackNode !== null
优先级已经改变,取消当前task,重新注册
整个调度流程:

Fiber树构造的基本概念
-
ReactElement
,Fiber
,DOM
的关系ReactElement
是通过书写jsx语法,编译器使用React.createElement转换Fiber
是通过ReactElement
进行创建的,Fiber
树是构造DOM
树的数据模型,Fiber
树的改动最终体现在DOM
树上 -
双缓冲技术
内存中存在两个
Fiber
树当前界面的fiber树挂载在
fiberRoot.current
正在构造的fiber树在
fiberRoot.alternate
正在构造的节点称为
workInProgress
-
优先级
有3种优先级,
renderLane,fiberLane
和updateLane
,只有命位运算&
有交集才处理,否则跳过renderLane
:当前任务要处理的优先级任务,判断下一次要处理的lane的顺序是pendingLanes,expiredLanes,suspendLanes,pingedLanes
renderLane
:当前任务要处理的优先级任务,判断下一次要处理的lane的顺序是pendingLanes,expiredLanes,suspendLanes,pingedLanes
fiberLane
:包含当前fiber所有update
的lane
的,fiber复用的条件是fiber.lanes
和renderLanes
没有交集updateLane
:setState
可以创建更新,fiber
中的updateQueue
是当前组件需要更新的更新,updateLane
是当前update
的优先级
-
栈帧
使用一组全局变量表示当前的活动记录,当
fiber
树构造被打断的时候可以依赖他们恢复上一次构造状态。prepareFreshStack
会刷新栈帧到初始状态,并创建HostRootFiber.alternate = workInProgress
Fiber树的初次构造
- 启动阶段:采用同步构造fiber树的方法
performSyncWorkOnRoot
- 获取当前构造的优先级
getNextLanes
,从所有已有的优先级获取最高的lane
,初次为最高NoLanes
- 清空栈帧
perpareFreshStack
,在构造fiber树开始都需要初始化,清空栈帧的时候会创建HostRootFiber.alternate
并将workInPregress
指向它 - 开始按照深度遍历的顺序构造
- 获取当前构造的优先级
- 构造阶段
- 循环构造,legacy和concurrent的区别是每次调用
performUnitOfWork
前都会检查是否需要让渡主线程给浏览器shouldYield
- 探寻阶段
beginWork
,从HostRootFiber.alternate
开始,根据不同的workInProgress.tag
类型执行不同的updateXXX
(根fiber→updateHostRoot
,普通DOM节点→updateHostComponent
),最后返回子节点fiber
,即为next
,next
不为空继续循环构造- 根据新的props
fiber.pendingProps
和setState得到的updatefiber.updateQueue
计算出新的状态fiber.memorizedProps
- 执行获取下一级的
ReactElement
,Class组件
实例挂载到fiber.stateNode
调用render
;函数组件
是直接执行。 - 根据实际情况设置
fiber.flags
- 调用
reconcilerChildren
生成次级 子节点的fiber
- 根据新的props
- 探寻阶段是如何遍历
updateQueue
的:baseQueue
保存着尚未处理的更新shared.pending
是setState
异步添加的更新- 将
shared.pending
追加baseQueue
末尾,根据renderLanes
遍历执行优先级够的update
,不够的保存到baseQueue中 - 在
update.callback
收集副作用,在commit
阶段执行。如果有副作用,标记workInProgress.flag |= Callback
- 回溯阶段:
completeUnitOfWork
- 调用
completeWork
,为HostComponent,HostText
创建DOM
实例,设置属性,添加事件,append
子节点的DOM
;设置fiber.stateNode
局部状态(如tag
为HostComponent,HostText
则是DOM
实例) - 将当前
fiber
的副作用队列(元素是fiber
)添加到父fiber
的副作用队列。判断fiber.flags
,如果当前节点有副作用,也将当前fiber添加到父fiber
的副作用队列 - 如果当前
fiber
节点有兄弟节点。将workInProgress
设为sibling
,继续beginWork
。否则向上回溯
- 调用
- 循环构造,legacy和concurrent的区别是每次调用

Fiber树的对比更新
-
更新入口
Class组件
的setState
Function组件
的dispatchAction
render
重复触发
-
创建更新并入队
创建
update
对象,并入队到当前的fiber
上,执行scheduleUpdateOnFiber
执行输入 -
调度执行
ensureRootIsScheduled
,首先markUpdateLaneFromFiberToRoot
将当前fiber的更新优先级向当前fiber.lanes
,当前fiber.alternate.lanes
和childLanes
合并,然后向父fiber
节点的lanes,alternate.lanes,childLanes
合并直到HostRootFiber
-
接下来和初次渲染一样,刷新栈帧,从
HostRoot
开始深度遍历 -
探寻阶段
beginWork
需要判断是不是老节点current ≠= null
,- 如果是老节点判断有没有更新(
lanes
和当前renderLanes
有重合部分),如果不需要更新直接进入completeUnitOfWork
阶段 - 如果
includeSomeLanes
说明子节点有更新,cloneChildFibers
克隆子节点并返回,进入updateXXX
更新fiber
。复用fiber
结构并重置flags,effects
属性 - 在
reconcilerChildren
阶段会对比新旧fiber
决定是否复用- 根据实际情况设置
fiber.flags
如新增Placement
,删除Delection
- 根据实际情况设置
- 需要删除的
fiber.flags = Delection
提前添加到父节点的effects链表
中,不会进入completeWork
阶段
- 如果是老节点判断有没有更新(
-
回溯阶段
completeUnitOfWork
和初次构建一致,遇到current≠=null
表明有老节点,标记fiber.flags |= Update
,在commit
阶段处理

Fiber树的渲染
- 调用
commitRoot
渲染 - 构建完成,即将渲染的
fiber
树在fiberRoot.finishedWork
上,fiber
树的特点:- 副作用列队挂在根节点上
finishedWork.firstEffect
上 - 最新页面的
DOM
对象挂载在fiber树首个HostComponent
的stateNode
上
- 副作用列队挂在根节点上
- 分为三个阶段:
渲染前
,渲染
和渲染后
- 渲染前
- 设置全局状态(更新fiber属性)
- 重置全局状态(
workInProgress
等) - 处理根节点副作用,如果根节点有副作用,将它添加到副作用链表尾部
- 渲染:分为3个阶段,
DOM
突变前,中,后 - DOM突变前,调用
commitBeforeMutationEffects
处理副作用,处理带有SnapShot,Passive
的fiber
节点SnapShot
标记可以有2种方式创建- 调用
Class
组件的getSnapShotBeforeUpdate
生命周期函数 - 调用
clearContainer
,清空容器(div#app
)
- 调用
Passive
标记是使用hook才会创建的,如useEffect
。通过scheduleCallback
异步执行副作用
- DOM突变,调用
commitMutationEffects
处理副作用,这里修改DOM
,界面会更新。处理带有ContentReset,Ref,Placement,update,Delection,Hydrating
的fiber节点。处理完成后会切换fiber.current = finishedWork
Ref
类型会清空,在CommitLayoutEffect
会重新赋值Placement,update,Delection,Hydrating
类型是新增,更新,删除节点,会通过与react-dom
包交互完成DOM
更新
- DOM突变后会处理带有
Ref,Update,Callback
的fiber
节点Ref
类型会重新进行ref赋值Update,Callabck
类型可以在ClassComponent
和HostComponet
出现- 调用
ClassComponent
的生命周期componentDidMount
和componetDidUpdate
,update
的回调update.callback
(如this.setState({}, callabck)
) HostComponet
带有Update
标记需要设置原生状态如focus
- 调用
- 渲染后:
- 遍历副作用链表,清空链表让
gc
回收 - 调用
ensureRootIsScheduled
,检查是否有异步的任务,发起异步调度(如componentDidMount
会再次setState
) - 检查同步的任务
flushSyncCallabckQueue
,如果有再次进入fiber构造循环
- 遍历副作用链表,清空链表让
状态与副作用
fiber对象中的属性有两类属性分别与自身状态
和副作用
相关
- 状态和副作用影响fiber构建与渲染不同阶段
- 状态相关:在
fiber树构造
阶段为节点提供确定的输入,直接影响子节点的生成,是静态
的功能 - 副作用相关:在
commitRoot
阶段,如果fiber有副作用,会同步/异步执行。是动态
的功能
- 状态相关:在
- 影响fiber状态和副作用的属性
- 状态:
pendingProps
最新传入的props
,和memorizedProps
对比可以得出属性是否改变memorizedProps
上一次子节点生成时用到的props
updateQueue
update
更新链表,发起更新创建的update
对象会保存在链表中memorizedState
上一次子节点生成保存的state
- 副作用
flags
标志位,表明副作用类型firstEffect
副作用链表,指向第一个fiber
对象lastEffect
副作用链表,指向最后一个fiber
对象nextEffect
副作用链表,指向下一个fiber
对象
- 状态:
生命周期函数
和hook
用于直接或间接控制上面两类属性-
Class组件
this.setState
发起更新请求,进入reconciler
阶段- 状态相关:
constructor
可以设置state - 状态相关:
getDerivedStateFromProps
让组件在修改props的时候更新state - 状态相关:
shouldComponentUpdate
每次state或props修改前调用 - 副作用相关:
getSnapshotBeforeUpdate
- 副作用相关:
componentDidMount
- 副作用相关:
componentDidUpdate
- 状态相关:
-
函数式组件
useState
的setXxx
发起更新请求,进入reconciler
阶段- 状态相关:
useState
可以修改Hook.memorizedState
- 副作用相关:
useEffect
flags是Passive | Update
,在beforeMutation
异步执行 - 副作用相关:
useLayoutState
flgas是Update
,在afterMutation
同步执行
- 状态相关:
-
Hook原理(概览)
- 状态hook与副作用hook区分
- 实现了状态持久化且没有没有副作用的hook:
useState,useReducer,useCallback,useMemo,useContext,useRef
等 - 会修改
flber.flags
的hook,useEffect,useLayoutEffect
- 组合:同时有状态和副作用2种属性:
useDeferredValue,useTransition,useSyncExternalStore
等
- 实现了状态持久化且没有没有副作用的hook:
Function
类型的fiber
节点通过updateFuncitonComponent
创建和更新,内部使用renderWithHooks
调用函数- 通过Hook API创建
hook
对象,Hook对象包含以下属性hook.memorizedState
保存在内存中的局部状态hook.baseState
由hook.baseQueue
合并后的状态hook.baseQueue
链表存储高于当前渲染优先级的update
对象hook.queue
链表存储所有优先级的update
对象hook.next
指向链表汇中下一个hook对象
- 当前
fiber
的hook
对象以链表的形式存储在fiber.memorizedState
中 fiber首次构造
时使用moutWorkInProgressHook
构造fiber.memorizedState
链表fiber对比更新
时使用updateWorkInProgressHook
构造fiber.memorizedState
链表,维护全局的currentHook
和workInProgressHook
,将current
上的hook
对象复制到alternate
上
Hook原理(状态)
- 状态初始化:
useState
使用mountState
,useReducer使
用mountReducer
创建hook
对象,进行- 初始化一个
hook
对象,包含上面5个属性 - 将
baseState
和memorizedState
设置为调用useState
的initailState
- 初始化
hook.queue
对象,pending
将来存储发起更新请求的后的update
对象 - 创建一个
dispatch
方法,调用这个方法发起更新请求dispatchAction
- 最后返回
[memorizedState, dispatch]
- 初始化一个
- 状态更新:调用dispatch函数发起一个调度更新请求
- 创建
update
对象 - 将
update
对象添加到hook.queue.pending
链表中 - 发起调度更新
scheduleUpdateOnFiber
,多次同步的dispatch
会因为调度更新判断渲染优先级相同 不会创建新的task
fiber对比更新
中调用updateReducer
- 将
hook.queue.pending
的update
链表拼接到hook.baseQueue
中 - 遍历
baseQueue
,渲染优先级高update
的执行,渲染优先级低update
的重新添加到baseQueue
等待下次执行 - 调用
reducer
计算得到memorizedState
- 创建
- 性能优化:
- 在
dispatchAction
的时候,如果当前创建的update
是queue.penging
中第一个update
(即本次调度中第一个setXxx
) - 直接计算
update
后的state
,记为eagerState
- 对比
eagerState
和memorizedState
,如果相同直接返回,不会请求调度
- 在

Hook原理(副作用)
-
初始化:
useEffect
,useLayoutEffect
可以创建hook
对象- 创建
hook
对象 - 将当前
fiber(workInProgress)
的flags
标记为fiberFlags
useEffect
是UpdateEffect | PassiveEffect
useLayoutEffect
是UpdateEffect
- 创建
effect
对象,放在循环链表上,挂载在hook.memorized
上;并添加到fiber
的链表尾部fiber.lastEffect
- 创建
-
effect
对象tag
:标记effect类型useEffect
是HookHasEffect | HookPassive
useLayoutEffect
是HookHasEffect | HookLayout
create
:传入的函数deps
:依赖项destory
:传入create
函数reutrn
的回调next
:指向下一个effect
-
处理回调,在
commitRoot
阶段处理useEffect
- 在
commitBeforeMutationEffects
阶段执行scheduleCallback
调度执行flushPassiveEffects
。 flushPassiveEffects
会遍历高于当前渲染优先级的effects
链表,将effect
对象的destory
和create
分别存储在两个数组中。- 先遍历执行
destory
数组,后遍历执行create
数组
- 在
useLayoutEffect
- 在
commitMutationEffects
先遍历effcts
链表,同步执行高于当前优先级的destory
- 在
commitLayoutEffects
遍历effcts
链表,同步执行高于当前优先级的create
- 在
-
fiber对比更新
阶段 -
desotry函数会直接复用
-
对比deps是否有变化,如果没有,新创建的
effect.tag
没有HookHasEffect
标志,不会在commitRoot
阶段执行 -
组件销毁,会在
commitUnmout
阶段遍历执行destory
函数

context原理
- 创建context:
- 将
pendingProps.value
保存到_currentValue
中 - 创建
Provider
和Consumer
两个ReactElement
对象
- 将
- 消费context
prepareToReadContext
进行重置操作readContext
构造一个contextItem
,将它push
到当前fiber
的workInProgress.dependencies
中,最后返回context._currentValue
- 更新context
- 对比
pendingProps
和memorizedProps
的新旧value
, - 如果没有变化进入
bailout
阶段对比子节点 - 如果变化,进入
propagateContextChange
- 向下查找
fiber
,找到所有fiber.dependencies
依赖该context
的fiber
(consumer
),创建update
入队,并标记节点的tag
为forceUpdate
。调度更新 - 从
consumer
节点,向上查找fiber
修改fiber.childLanes
表明子节点有改动
- 对比
合成事件
-
不能冒泡的事件
scroll,resize,focue,blur,mouseleave,mouseenter
-
采用事件委托在根节点注册事件,不能代理的特殊处理
-
事件绑定:
listenToAllSupportedEvents
绑定支持的事件类型,完成原生事件代理addEventBubbleListener
监听冒泡事件addEventCaptureListener
监听捕获事件
-
调度函数
dispatchEvent
,React将不同的事件化为3个等级,采用不同的调度函数disPatchDiscreateEvent
:优先级最高,如click,input
dispatchUserBlockingEvent
:优先级次之,如scroll,drag
dispatchContinuoutEvent
:优先级最低,如load,animate
-
事件触发,使用
dispatchEvent
触发事件attempToDispatchEvent
关联fiber
- 定位到原生
DOM
,调用getEventTarget
- 获取
DOM
对应的fiber
,getClosestInstanceFromNode
- 通过插件系统派发事件
dispatchEventForPluginSystem
- 定位到原生
- 从
targetFiber
上遍历,获取所有对应事件(如onClick
)的事件回调,回调保存在fiber.memorizedProps
中(如fiber.memorizedProps.onClick
) - 创建合成事件
SyntheticEvent
,添加到派发队列dispatchQueue
中。合成事件主要抹平了不同平台的差异 - 执行派发,因为
dispatchQueue
是从子节点向父节点收集,capture
倒序触发,bubble
顺序触发
-
综上:主要步骤是
- 监听原生事件,找到
target
元素对应的fiber
- 向上遍历
fiber
树,收集所有监听本事件的listener
- 构建合成事件,遍历
listener
数组派发(触发)事件
- 监听原生事件,找到