事件优先级
React 的合成事件系统与事件优先级机制 共同确保了用户交互的即时响应和复杂渲染任务的高效调度。
事件优先级 |
特点 |
离散事件(Discrete Events) |
如 优先级最高,立即处理,不会被中断 |
连续事件(Continuous Events) |
如 优先级中等,允许被更高优先级中断 |
默认事件(Default Events) |
如 优先级较低,可被用户交互中断 |
空闲事件(Idle Events) |
如非关键 UI 更新、统计数据上报 仅在浏览器空闲时执行 |
事件优先级模型:
// NoEventPriority 表示没有事件优先级
const NoEventPriority: EventPriority = NoLane;
// DiscreteEventPriority 表示离散事件优先级,被映射到 SyncLane。离散事件通常是一些用户交互事件,如点击、键盘输入等,这些事件需要立即处理,以保证用户操作的即时响应。SyncLane 是同步车道,在这个车道上的更新任务会立即执行。
const DiscreteEventPriority: EventPriority = SyncLane;
// ContinuousEventPriority 表示连续事件优先级,对应 InputContinuousLane。连续事件一般是一些连续的用户交互,如滚动、拖拽等。InputContinuousLane 是用于处理连续输入事件的车道,这些事件需要在一定的时间内持续处理,以保证交互的流畅性。
const ContinuousEventPriority: EventPriority = InputContinuousLane;
// DefaultEventPriority 是默认事件优先级,映射到 DefaultLane。当没有明确指定事件优先级时,会使用默认优先级。DefaultLane 是一个通用的车道,用于处理大多数普通的更新任务
const DefaultEventPriority: EventPriority = DefaultLane;
// IdleEventPriority 表示空闲事件优先级,与 IdleLane 对应。空闲事件是那些可以在浏览器空闲时执行的任务,例如一些非关键的渲染更新或数据处理。IdleLane 上的任务会在浏览器有空闲时间时才会被执行,以避免影响用户交互的性能。
const IdleEventPriority: EventPriority = IdleLane;
dispatchDiscreteEvent (离散事件)
dispatchDiscreteEvent
函数的主要作用是调度离散事件。
离散事件通常是一些需要立即处理的用户交互事件,如点击、键盘输入等。
该函数会在处理离散事件时,临时设置当前的更新优先级为离散事件优先级,以确保事件能够得到及时处理,处理完成后再恢复之前的更新优先级。
函数参数含义:
domEventName
:类型为DOMEventName
,表示要调度的原生 DOM 事件名称。eventSystemFlags
:类型为EventSystemFlags
,是一个用于存储事件系统相关标志的变量,用于表示事件的不同状态或特性。container
:类型为EventTarget
,代表事件发生的容器,通常是一个 DOM 元素。nativeEvent
:类型为AnyNativeEvent
,是原生的 DOM 事件对象,包含了事件的详细信息等。
function dispatchDiscreteEvent(
domEventName: DOMEventName,
eventSystemFlags: EventSystemFlags,
container: EventTarget,
nativeEvent: AnyNativeEvent,
) {
// ReactSharedInternals.T 可能是 React 内部用于管理过渡状态的一个变量。
const prevTransition = ReactSharedInternals.T;
// 重置,确保离散事件(如点击、输入)不会触发过渡动画,保证即时响应。
ReactSharedInternals.T = null;
// 获取当前更新的优先级
const previousPriority = getCurrentUpdatePriority();
try {
// 调用 setCurrentUpdatePriority 函数将当前的更新优先级设置为 DiscreteEventPriority,表示当前正在处理离散事件,需要立即处理。
setCurrentUpdatePriority(DiscreteEventPriority);
// 调度事件
dispatchEvent(domEventName, eventSystemFlags, container, nativeEvent);
} finally {
// 恢复更新优先级和过度状态
setCurrentUpdatePriority(previousPriority);
ReactSharedInternals.T = prevTransition;
}
}
function getCurrentUpdatePriority(): EventPriority {
return ReactDOMSharedInternals.p; /* currentUpdatePriority */
}
function setCurrentUpdatePriority(
newPriority: EventPriority,
IntentionallyUnusedArgument?: empty,
): void {
ReactDOMSharedInternals.p /* currentUpdatePriority */ = newPriority;
}
dispatchContinuousEvent (连续事件)
dispatchContinuousEvent
函数主要用于调度连续事件。
连续事件通常指的是那些连续发生的用户交互事件,例如滚动、拖拽等操作产生的事件。
该函数的核心逻辑是在处理连续事件时,临时调整当前的更新优先级为连续事件优先级,以确保这些连续事件能按照合适的优先级进行处理,处理完成后再恢复之前的更新优先级和过渡状态。
函数参数含义:
domEventName
:类型为DOMEventName
,表示要调度的原生 DOM 事件名称。eventSystemFlags
:类型为EventSystemFlags
,是一个用于存储事件系统相关标志的变量,用于表示事件的不同状态或特性。container
:类型为EventTarget
,代表事件发生的容器,通常是一个 DOM 元素。nativeEvent
:类型为AnyNativeEvent
,是原生的 DOM 事件对象,包含了事件的详细信息等。
function dispatchContinuousEvent(
domEventName: DOMEventName,
eventSystemFlags: EventSystemFlags,
container: EventTarget,
nativeEvent: AnyNativeEvent,
) {
const prevTransition = ReactSharedInternals.T;
ReactSharedInternals.T = null;
const previousPriority = getCurrentUpdatePriority();
try {
setCurrentUpdatePriority(ContinuousEventPriority);
// 调度事件
dispatchEvent(domEventName, eventSystemFlags, container, nativeEvent);
} finally {
setCurrentUpdatePriority(previousPriority);
ReactSharedInternals.T = prevTransition;
}
}
dispatchEvent(默认事件)
dispatchEvent
函数是 React 事件系统中用于调度原生 DOM 事件的核心函数。它会根据事件的不同状态和条件,决定如何处理事件,包括检查事件是否启用、查找阻止事件的实例、调用插件事件系统进行事件分发、处理连续事件的排队等。
函数参数含义:
domEventName
:类型为DOMEventName
,表示要调度的原生 DOM 事件名称。eventSystemFlags
:类型为EventSystemFlags
,是一个用于存储事件系统相关标志的变量,用于表示事件的不同状态或特性。container
:类型为EventTarget
,代表事件发生的容器,通常是一个 DOM 元素。nativeEvent
:类型为AnyNativeEvent
,是原生的 DOM 事件对象,包含了事件的详细信息等。
function dispatchEvent(
domEventName: DOMEventName,
eventSystemFlags: EventSystemFlags,
targetContainer: EventTarget,
nativeEvent: AnyNativeEvent,
): void {
// 如果 _enabled 标志为 false,表示事件系统未启用,直接返回,不进行后续的事件处理。
if (!_enabled) {
return;
}
// findInstanceBlockingEvent 函数,根据原生事件对象 nativeEvent 查找是否存在阻止该事件的实例。如果存在,返回该实例;如果不存在,返回 null。
let blockedOn = findInstanceBlockingEvent(nativeEvent);
// 如果 blockedOn 为 null,说明没有实例阻止该事件。
if (blockedOn === null) {
// 调用 dispatchEventForPluginEventSystem 函数,将事件信息传递给插件事件系统进行处理。
dispatchEventForPluginEventSystem(
domEventName,
eventSystemFlags,
nativeEvent,
return_targetInst,
targetContainer,
);
// 调用 clearIfContinuousEvent 函数,根据事件名称和原生事件对象,清除连续事件的相关状态。最后返回,结束事件处理。
clearIfContinuousEvent(domEventName, nativeEvent);
return;
}
// 连续事件排队处理
// 如果 blockedOn 不为 null,调用 queueIfContinuousEvent 函数尝试将事件排队。
if (
queueIfContinuousEvent(
blockedOn,
domEventName,
eventSystemFlags,
targetContainer,
nativeEvent,
)
) {
// 如果排队成功,调用 nativeEvent.stopPropagation() 阻止事件继续传播,并返回。
nativeEvent.stopPropagation();
return;
}
// We need to clear only if we didn't queue because
// queueing is accumulative.
// 如果排队失败,调用 clearIfContinuousEvent 函数清除连续事件的相关状态。
clearIfContinuousEvent(domEventName, nativeEvent);
// This is not replayable so we'll invoke it but without a target,
// in case the event system needs to trace it.
// 其他情况分发
// 调用 dispatchEventForPluginEventSystem 函数分发事件,但将目标实例设置为 null,以便事件系统进行跟踪
dispatchEventForPluginEventSystem(
domEventName,
eventSystemFlags,
nativeEvent,
null,
targetContainer,
);
}
findInstanceBlockingEvent
findInstanceBlockingEvent
是 React 内部用于在事件处理流程中 定位阻塞渲染的实例 的核心函数。
其核心作用是:
- 通过事件目标(
nativeEventTarget
)找到对应的 Fiber 实例(如Container
或SuspenseInstance
) - 识别哪些组件实例会阻塞事件的传播或渲染(例如 Suspense 边界、根容器)
function findInstanceBlockingEvent(
nativeEvent: AnyNativeEvent,
): null | Container | SuspenseInstance {
const nativeEventTarget = getEventTarget(nativeEvent);
return findInstanceBlockingTarget(nativeEventTarget);
}
工具函数之 getEventTarget
getEventTarget
是 React 内部用于 规范化事件目标(Event Target) 的工具函数。它从原生事件对象中提取实际触发事件的 DOM 节点,并处理以下边缘情况:
- 跨浏览器兼容性:兼容不同浏览器对事件目标的不同实现(如
target
或srcElement
) - SVG 使用元素处理:处理 SVG 中
<use>
元素的特殊情况 - 文本节点归一化:将文本节点(
TEXT_NODE
)转换为其父元素节点
function getEventTarget(nativeEvent) {
// 已弃用的 Event.srcElement 是 Event.target 属性的别名。
// https://developer.mozilla.org/zh-CN/docs/Web/API/Event/srcElement
let target = nativeEvent.target || nativeEvent.srcElement || window;
// correspondingUseElement属性可以获取到原始SVG元素实例
if (target.correspondingUseElement) {
target = target.correspondingUseElement;
}
return target.nodeType === TEXT_NODE ? target.parentNode : target;
}
<svg width="100" height="100">
<defs>
<circle id="circle" cx="50" cy="50" r="40" stroke="black" stroke-width="3" fill="red"/>
</defs>
<use xlink:href="#circle" id="usedCircle" onclick="clickHandler(event)"/>
</svg>
<script>
function clickHandler(event) {
var target = event.target;
if (target.correspondingUseElement) {
target = target.correspondingUseElement;
}
console.log(target); // 这里可以获取到原始的circle元素
}
</script>
工具函数之 findInstanceBlockingTarget
findInstanceBlockingTarget
是 React 内部用于 查找阻塞事件处理的 Fiber 实例 的核心函数。其主要作用是在事件触发时,确定是否存在 Suspense 边界或根容器阻塞事件的正常处理,从而实现对异步组件加载状态的控制。
function findInstanceBlockingTarget(
targetNode: Node,
): null | Container | SuspenseInstance {
// 存储目标实例
return_targetInst = null;
// 从 DOM 节点获取对应的 React Fiber 实例(通过 internalInstanceKey 关联)
let targetInst = getClosestInstanceFromNode(targetNode);
if (targetInst !== null) {
// getNearestMountedFiber 会向上查找最近的已挂载 Fiber。如果返回 null,说明整棵树已卸载,直接忽略事件。
const nearestMounted = getNearestMountedFiber(targetInst);
if (nearestMounted === null) {
// This tree has been unmounted already. Dispatch without a target.
targetInst = null;
} else {
const tag = nearestMounted.tag;
if (tag === SuspenseComponent) {
// 如果目标节点属于 Suspense 组件,返回其 Suspense 实例(如 fallback DOM 节点)
const instance = getSuspenseInstanceFromFiber(nearestMounted);
// 返回 Suspense 实例,阻止事件传播
if (instance !== null) {
return instance;
}
targetInst = null;
} else if (tag === HostRoot) {
// 根节点本身不处理事件,直接忽略。
targetInst = null;
// 若最近已挂载节点与目标实例不一致
} else if (nearestMounted !== targetInst) {
// 说明目标实例已卸载或不在当前渲染树中
targetInst = null;
}
}
}
return_targetInst = targetInst;
// 默认返回 null,允许事件继续传播
return null;
}
工具函数之 getClosestInstanceFromNode
getClosestInstanceFromNode
是 React 内部用于 从 DOM 节点查找最近的 Fiber 实例 的核心函数。其主要作用是:
- 通过
internalInstanceKey
关联的属性快速定位 Fiber - 处理非 React 直接管理的 DOM 节点(向上遍历父节点)
- 特殊处理 Suspense 边界和脱水状态的组件
function getClosestInstanceFromNode(targetNode: Node): null | Fiber {
// internalInstanceKey:React 在 DOM 节点上存储的 Fiber 引用(如 __reactFiber$...)。
let targetInst = (targetNode: any)[internalInstanceKey];
if (targetInst) {
// Don't return HostRoot or SuspenseComponent here.
return targetInst;
}
// If the direct event target isn't a React owned DOM node, we need to look
// to see if one of its parents is a React owned DOM node.
// 递归向上查找最近的 React 父节点。
let parentNode = targetNode.parentNode;
while (parentNode) {
targetInst =
// internalContainerInstanceKey:用于 Portal 容器节点(如 ReactDOM.createPortal 的挂载点)。
(parentNode: any)[internalContainerInstanceKey] ||
(parentNode: any)[internalInstanceKey];
if (targetInst) {
const alternate = targetInst.alternate;
// 检查当前 Fiber 或其备用(alternate)Fiber 是否有子节点
if (
targetInst.child !== null ||
(alternate !== null && alternate.child !== null)
) {
// Next we need to figure out if the node that skipped past is
// nested within a dehydrated boundary and if so, which one.
// 从 DOM 节点向上查找最近的 Suspense 边界节点(通过 __reactSuspenseInstance$... 标识)。
let suspenseInstance = getParentSuspenseInstance(targetNode);
while (suspenseInstance !== null) {
const targetSuspenseInst = suspenseInstance[internalInstanceKey];
// 返回 Suspense 边界的 Fiber
if (targetSuspenseInst) {
return targetSuspenseInst;
}
suspenseInstance = getParentSuspenseInstance(suspenseInstance);
}
}
return targetInst;
}
// 如果当前父节点未找到 Fiber,继续向上遍历,直到 parentNode 为 null(到达文档根节点)。
targetNode = parentNode;
parentNode = targetNode.parentNode;
}
return null;
}
工具函数之 getParentSuspenseInstance
getParentSuspenseInstance
是 React 内部用于 查找最近的 Suspense 边界实例 的工具函数。其核心作用是:
- 通过 DOM 节点的 注释标记 识别 Suspense 边界
- 处理嵌套 Suspense 的层级关系(通过
depth
计数) - 为事件处理、水合过程提供 Suspense 上下文信息
function getParentSuspenseInstance(
targetInstance: Node,
): null | SuspenseInstance {
// 返回当前节点的前一个兄弟节点,没有则返回null.
let node = targetInstance.previousSibling;
// 记录当前嵌套层级,确保找到最近的 Suspense 边界
let depth = 0;
// 向左遍历兄弟节点(从当前节点向前查找)
while (node) {
if (node.nodeType === COMMENT_NODE) {
const data = ((node: any).data: string);
// 处理开始标记
if (
data === SUSPENSE_START_DATA ||
data === SUSPENSE_FALLBACK_START_DATA ||
data === SUSPENSE_PENDING_START_DATA
) {
// 当 depth=0 时,直接返回当前注释节点(最近的 Suspense 边界)
if (depth === 0) {
return ((node: any): SuspenseInstance);
} else {
// 当 depth>0 时,说明遇到了更内层的 Suspense 边界,depth 减 1
depth--;
}
// 处理结束标记
} else if (data === SUSPENSE_END_DATA) {
// 遇到结束标记时,depth 加 1,平衡嵌套层级
depth++;
}
}
node = node.previousSibling;
}
return null;
}
工具函数之 getNearestMountedFiber
getNearestMountedFiber
是 React 内部用于 查找最近已挂载 Fiber 节点 的核心函数。其核心作用是:
- 处理未挂载或正在挂载的节点:通过向上遍历,找到最近的已挂载父节点
- 识别已卸载的组件树:若整棵树已卸载,返回
null
- 支持并发渲染:正确处理插入(Placement)和水合(Hydrating)状态的节点
function getNearestMountedFiber(fiber: Fiber): null | Fiber {
let node = fiber;
// 最近挂载节点
let nearestMounted: null | Fiber = fiber;
// 无 alternate 的节点是新创建的,可能尚未挂载
if (!fiber.alternate) {
let nextNode: Fiber = node;
do {
node = nextNode;
// 检查节点的 flags 是否包含 Placement 或 Hydrating
if ((node.flags & (Placement | Hydrating)) !== NoFlags) {
// 最近已挂载节点为其父节点(node.return)
nearestMounted = node.return;
}
nextNode = node.return;
} while (nextNode);
} else {
// 有 alternate 的节点已存在于当前渲染树中
while (node.return) {
// 直接向上遍历至根节点
node = node.return;
}
}
// 若最终到达 HostRoot,说明组件树已挂载,返回最近已挂载节点
if (node.tag === HostRoot) {
return nearestMounted;
}
// If we didn't hit the root, that means that we're in an disconnected tree
// that has been unmounted.
// 若未到达根节点(如遍历到 detached 节点),说明组件树已卸载,返回 null
return null;
}
工具函数之 isRootDehydrated
isRootDehydrated
是 React 内部用于 判断根 Fiber 节点是否处于脱水状态(Dehydrated) 的工具函数。在服务端渲染(SSR)场景中,React 会将渲染结果序列化为 HTML 发送到客户端,这些预渲染的内容被标记为 "脱水" 状态。客户端激活(Hydration)阶段,React 通过此函数判断根节点是否需要进行水合操作。
function isRootDehydrated(root: FiberRoot): boolean {
// root.current:指向当前已提交的根 Fiber 节点
// memoizedState:存储 Fiber 节点的状态信息,对于根节点,包含渲染状态、水合标记等
const currentState: RootState = root.current.memoizedState;
// isDehydrated 为 true 时,表示根节点包含服务端渲染的脱水内容,需要进行水合
// isDehydrated 为 false 时,表示根节点是纯客户端渲染,无需水合
return currentState.isDehydrated;
}
工具函数之 getSuspenseInstanceFromFiber
getSuspenseInstanceFromFiber
是 React 内部用于从 Fiber
节点中提取 Suspense 实例(SuspenseInstance
) 的工具函数。其核心作用是在服务端渲染(SSR)或客户端水合(Hydration)过程中,识别并获取与 Fiber
节点关联的 Suspense 状态,以便处理异步组件的加载状态和阻塞逻辑。
function getSuspenseInstanceFromFiber(
fiber: Fiber,
): null | SuspenseInstance {
// 仅处理 Suspense 类型的 Fiber 节点
if (fiber.tag === SuspenseComponent) {
let suspenseState: SuspenseState | null = fiber.memoizedState;
// 若当前 Fiber 状态为空,尝试从备用 Fiber(alternate)获取
if (suspenseState === null) {
// fiber.alternate 指向 current Fiber(已提交的渲染状态)
const current = fiber.alternate;
if (current !== null) {
suspenseState = current.memoizedState;
}
}
if (suspenseState !== null) {
// 返回脱水状态的 Suspense 实例
return suspenseState.dehydrated;
}
}
return null;
}
dispatchEventForPluginEventSystem
dispatchEventForPluginEventSystem
函数是 React 事件系统中用于调度事件到插件事件系统的核心函数。
它的主要任务是根据事件的相关标志和目标实例,对事件进行预处理,最后在批量更新的上下文中调用 dispatchEventsForPlugins
函数来实际分发事件。
函数参数含义:
domEventName
:类型为DOMEventName
,表示原生 DOM 事件的名称,如'click'
、'keydown'
等。eventSystemFlags
:类型为EventSystemFlags
,是一个用于存储事件系统相关标志的变量,用于表示事件的不同状态或特性。nativeEvent
:类型为AnyNativeEvent
,是原生的 DOM 事件对象,包含了事件发生时的详细信息。targetInst
:类型为null | Fiber
,表示事件的目标 Fiber 实例,可能为null
。targetContainer
:类型为EventTarget
,表示事件发生的目标容器,通常是一个 DOM 元素。
function dispatchEventForPluginEventSystem(
domEventName: DOMEventName,
eventSystemFlags: EventSystemFlags,
nativeEvent: AnyNativeEvent,
targetInst: null | Fiber,
targetContainer: EventTarget,
): void {
// 初始化祖先元素
let ancestorInst = targetInst;
// 如果事件不是处理非托管节点的事件((eventSystemFlags & IS_EVENT_HANDLE_NON_MANAGED_NODE) === 0),并且不是非委托事件((eventSystemFlags & IS_NON_DELEGATED) === 0),则进入事件委托相关的处理逻辑。
if (
(eventSystemFlags & IS_EVENT_HANDLE_NON_MANAGED_NODE) === 0 &&
(eventSystemFlags & IS_NON_DELEGATED) === 0
) {
const targetContainerNode = ((targetContainer: any): Node);
if (targetInst !== null) {
// 声明一个变量 node 并初始化为 targetInst,targetInst 是事件的目标 Fiber 实例。后续会通过不断更新 node 的值来遍历 Fiber 树。
let node: null | Fiber = targetInst;
// 通过一个 while 循环遍历 Fiber 树
// 使用一个无限循环 while (true) 来遍历 Fiber 树,并且给这个循环添加了一个标签 mainLoop,方便后续使用 continue 或 break 语句控制循环。
mainLoop: while (true) {
// 如果 node 为 null,说明已经遍历到 Fiber 树的顶部,此时直接返回。
if (node === null) {
return;
}
// fiber节点的类型
const nodeTag = node.tag;
// HostRoot表示根fiber
if (nodeTag === HostRoot || nodeTag === HostPortal) {
// 获取真实容器的信息
let container = node.stateNode.containerInfo;
// 调用 isMatchingRootContainer 函数检查这个容器是否与目标容器 targetContainerNode 匹配。
if (isMatchingRootContainer(container, targetContainerNode)) {
// 如果匹配,则跳出 mainLoop 循环。
break;
}
// 如果当前 Fiber 节点是 HostPortal 类型,从其 return 节点(即父节点)开始向上遍历祖先节点。
if (nodeTag === HostPortal) {
// node的父节点
let grandNode = node.return;
while (grandNode !== null) {
const grandTag = grandNode.tag;
// 对于每个 HostRoot 或 HostPortal 类型的祖先节点,检查其对应的容器是否与目标容器匹配。如果匹配,则直接返回。
if (grandTag === HostRoot || grandTag === HostPortal) {
const grandContainer = grandNode.stateNode.containerInfo;
if (
isMatchingRootContainer(grandContainer, targetContainerNode)
) {
return;
}
}
grandNode = grandNode.return;
}
}
while (container !== null) {
// 调用 getClosestInstanceFromNode 函数获取与容器对应的 Fiber 节点 parentNode。
const parentNode = getClosestInstanceFromNode(container);
// 如果 parentNode 为 null,则直接返回。
if (parentNode === null) {
return;
}
const parentTag = parentNode.tag;
// 如果 parentNode 的类型是 HostComponent、HostText、HostHoistable 或 HostSingleton,则更新 node 和 ancestorInst 为 parentNode
if (
parentTag === HostComponent ||
parentTag === HostText ||
parentTag === HostHoistable ||
parentTag === HostSingleton
) {
node = ancestorInst = parentNode;
// 使用 continue mainLoop 语句跳转到 mainLoop 循环的开始处继续遍历。
continue mainLoop;
}
container = container.parentNode;
}
}
// 移到下一个节点
node = node.return;
}
}
}
// 批量更新并分发事件
batchedUpdates(() =>
dispatchEventsForPlugins(
domEventName,
eventSystemFlags,
nativeEvent,
ancestorInst,
targetContainer,
),
);
}
工具函数之 isMatchingRootContainer
isMatchingRootContainer
是 React 内部用于 判断两个 DOM 容器是否匹配 的工具函数。
function isMatchingRootContainer(
grandContainer: Element,
targetContainer: EventTarget,
): boolean {
return (
grandContainer === targetContainer ||
// 注释节点特殊处理
(grandContainer.nodeType === COMMENT_NODE &&
grandContainer.parentNode === targetContainer)
);
}
工具函数之 batchedUpdates
batchedUpdates
是 React 中用于 批量处理更新(Batched Updates) 的核心函数,主要目的是 将多个状态更新合并为一次渲染,避免不必要的重复渲染,从而优化性能。
function batchedUpdates(fn, a, b) {
if (isInsideEventHandler) {
// 如果当前已经在批量更新中,直接执行函数(不重新进入批量模式)
return fn(a, b);
}
// isInsideEventHandler:全局标志位,表示是否正在处理事件回调
isInsideEventHandler = true;
try {
// 调用实际批量逻辑
return batchedUpdatesImpl(fn, a, b);
} finally {
// 恢复标志位
isInsideEventHandler = false;
// 清理可能的残留任务
finishEventHandler();
}
}
工具函数之 batchedUpdatesImpl
batchedUpdates
是 React 内部用于 批量处理状态更新 的核心函数,主要作用是将多个状态更新(如 setState
)合并为单个渲染流程,以提高性能。
function batchedUpdates<A, R>(fn: A => R, a: A): R {
// 并发模式
if (disableLegacyMode) {
// 直接执行,无批量处理
return fn(a);
} else {
// 传统模式
// 标记当前处于批量上下文
const prevExecutionContext = executionContext;
executionContext |= BatchedContext;
try {
return fn(a);
} finally {
executionContext = prevExecutionContext;
}
}
}
dispatchEventsForPlugins
dispatchEventsForPlugins
函数是 React 事件系统中负责将事件分发给各个插件进行处理的核心函数。
该函数的主要工作流程是先获取事件的目标元素,然后创建一个空的调度队列,接着调用 extractEvents
函数从事件中提取相关信息并填充到调度队列中,最后调用 processDispatchQueue
函数来处理这个调度队列,从而完成事件的分发和处理。
函数参数含义:
domEventName
:表示原生 DOM 事件的名称,例如'click'
、'keydown'
等。eventSystemFlags
:是一个用于存储事件系统相关标志的变量,这些标志可以表示事件的不同状态或特性,例如是否为捕获阶段、是否为被动事件等。nativeEvent
:原生的 DOM 事件对象,包含了事件发生时的详细信息,如鼠标位置、按键信息等。ancestorInst
:表示在 React 的 Fiber 树中找到的合适的祖先 Fiber 实例,事件会在这个祖先实例及其子树中进行分发处理。targetContainer
:表示事件发生的目标容器,通常是一个 DOM 元素。
function dispatchEventsForPlugins(
domEventName: DOMEventName,
eventSystemFlags: EventSystemFlags,
nativeEvent: AnyNativeEvent,
targetInst: null | Fiber,
targetContainer: EventTarget,
): void {
// 获取目标元素
const nativeEventTarget = getEventTarget(nativeEvent);
// 创建调度队列
const dispatchQueue: DispatchQueue = [];
extractEvents(
dispatchQueue,// 调度队列
domEventName,// 原生事件名称
targetInst,// 目标 Fiber 实例
nativeEvent,// 原生事件对象
nativeEventTarget,// 事件目标元素
eventSystemFlags,// 事件系统标志
targetContainer,// 目标容器
);
// 处理调度队列
// processDispatchQueue 函数会遍历调度队列,根据队列中的信息依次调用相应的事件处理程序,从而完成事件的分发和处理。
processDispatchQueue(dispatchQueue, eventSystemFlags);
}
工具函数之 extractEvents
extractEvents
函数的主要作用是从原生 DOM 事件中提取事件信息,并将这些信息添加到调度队列中。它通过调用不同的事件插件的 extractEvents
方法来完成这一任务,这些插件负责处理不同类型的事件。首先会调用 SimpleEventPlugin
插件,然后根据 eventSystemFlags
标志判断是否需要处理其他的填充(polyfill)插件。
函数参数含义:
dispatchQueue
:类型为DispatchQueue
,是一个用于存储事件调度信息的队列,后续会将提取的事件信息添加到这个队列中。domEventName
:类型为DOMEventName
,表示原生 DOM 事件的名称,如'click'
、'keydown'
等。targetInst
:类型为null | Fiber
,是事件的目标 Fiber 实例,可能为null
。nativeEvent
:类型为AnyNativeEvent
,是原生的 DOM 事件对象,包含了事件发生时的详细信息。nativeEventTarget
:类型为null | EventTarget
,是事件的目标元素,可能为null
。eventSystemFlags
:类型为EventSystemFlags
,是一个存储事件系统相关标志的变量,用于表示事件的不同状态或特性。targetContainer
:类型为EventTarget
,是事件发生的目标容器,通常是一个 DOM 元素。
import * as BeforeInputEventPlugin from './plugins/BeforeInputEventPlugin';
import * as ChangeEventPlugin from './plugins/ChangeEventPlugin';
import * as EnterLeaveEventPlugin from './plugins/EnterLeaveEventPlugin';
import * as SelectEventPlugin from './plugins/SelectEventPlugin';
import * as SimpleEventPlugin from './plugins/SimpleEventPlugin';
import * as FormActionEventPlugin from './plugins/FormActionEventPlugin';
function extractEvents(
dispatchQueue: DispatchQueue,
domEventName: DOMEventName,
targetInst: null | Fiber,
nativeEvent: AnyNativeEvent,
nativeEventTarget: null | EventTarget,
eventSystemFlags: EventSystemFlags,
targetContainer: EventTarget,
) {
// 调用 SimpleEventPlugin 插件的 extractEvents 方法,将事件的相关信息传递给该方法,让其从事件中提取信息并添加到调度队列 dispatchQueue 中。SimpleEventPlugin 可能负责处理一些简单的、通用的事件类型。
SimpleEventPlugin.extractEvents(
dispatchQueue,
domEventName,
targetInst,
nativeEvent,
nativeEventTarget,
eventSystemFlags,
targetContainer,
);
const shouldProcessPolyfillPlugins =
(eventSystemFlags & SHOULD_NOT_PROCESS_POLYFILL_EVENT_PLUGINS) === 0;
// 需要填充插件
if (shouldProcessPolyfillPlugins) {
EnterLeaveEventPlugin.extractEvents(
dispatchQueue,
domEventName,
targetInst,
nativeEvent,
nativeEventTarget,
eventSystemFlags,
targetContainer,
);
ChangeEventPlugin.extractEvents(
dispatchQueue,
domEventName,
targetInst,
nativeEvent,
nativeEventTarget,
eventSystemFlags,
targetContainer,
);
SelectEventPlugin.extractEvents(
dispatchQueue,
domEventName,
targetInst,
nativeEvent,
nativeEventTarget,
eventSystemFlags,
targetContainer,
);
BeforeInputEventPlugin.extractEvents(
dispatchQueue,
domEventName,
targetInst,
nativeEvent,
nativeEventTarget,
eventSystemFlags,
targetContainer,
);
FormActionEventPlugin.extractEvents(
dispatchQueue,
domEventName,
targetInst,
nativeEvent,
nativeEventTarget,
eventSystemFlags,
targetContainer,
);
}
}
clearIfContinuousEvent
clearIfContinuousEvent
是 React 事件系统中用于 清理连续事件队列 的辅助函数,主要解决 某些特殊事件类型(如 focus
、drag
、mouse
等)在快速连续触发时可能导致的逻辑冲突问题。
function clearIfContinuousEvent(
domEventName: DOMEventName,
nativeEvent: AnyNativeEvent,
): void {
switch (domEventName) {
case 'focusin':
case 'focusout':
// 清空queuedFocus队列
queuedFocus = null;
break;
case 'dragenter':
case 'dragleave':
// 清空queuedDrag队列
queuedDrag = null;
break;
case 'mouseover':
case 'mouseout':
// 清空queuedMouse队列
queuedMouse = null;
break;
// https://developer.mozilla.org/zh-CN/docs/Web/API/Element/pointerover_event
case 'pointerover':
case 'pointerout': {
const pointerId = ((nativeEvent: any): PointerEvent).pointerId;
queuedPointers.delete(pointerId);
break;
}
// https://developer.mozilla.org/zh-CN/docs/Web/API/Element/gotpointercapture_event
// https://developer.mozilla.org/zh-CN/docs/Web/API/Element/lostpointercapture_event
case 'gotpointercapture':
case 'lostpointercapture': {
const pointerId = ((nativeEvent: any): PointerEvent).pointerId;
queuedPointerCaptures.delete(pointerId);
break;
}
}
}
type AnyNativeEvent = Event | KeyboardEvent | MouseEvent | TouchEvent;
type PointerEvent = Event & {
pointerId: number,
relatedTarget: EventTarget | null,
};
let queuedFocus: null | QueuedReplayableEvent = null;
let queuedDrag: null | QueuedReplayableEvent = null;
let queuedMouse: null | QueuedReplayableEvent = null;
type QueuedReplayableEvent = {
blockedOn: null | Container | SuspenseInstance,
domEventName: DOMEventName,
eventSystemFlags: EventSystemFlags,
nativeEvent: AnyNativeEvent,
targetContainers: Array<EventTarget>,
};
const queuedPointers: Map<number, QueuedReplayableEvent> = new Map();
const queuedPointerCaptures: Map<number, QueuedReplayableEvent> = new Map();
queueIfContinuousEvent
queueIfContinuousEvent
是 React 事件系统中用于 管理连续事件队列 的核心函数,专门处理那些需要 特殊跟踪的持续性事件(如 focus
、drag
、mouse
和 pointer
事件)。它的核心作用是将这些事件加入对应的全局队列。
function queueIfContinuousEvent(
blockedOn: null | Container | SuspenseInstance,
domEventName: DOMEventName,
eventSystemFlags: EventSystemFlags,
targetContainer: EventTarget,
nativeEvent: AnyNativeEvent,
): boolean {
// Instead of mutating we could clone the event.
switch (domEventName) {
case 'focusin': {
const focusEvent = ((nativeEvent: any): FocusEvent);
queuedFocus = accumulateOrCreateContinuousQueuedReplayableEvent(
queuedFocus,
blockedOn,
domEventName,
eventSystemFlags,
targetContainer,
focusEvent,
);
// 成功加入队列,返回true
return true;
}
case 'dragenter': {
const dragEvent = ((nativeEvent: any): DragEvent);
// 合并或创建可重放事件
queuedDrag = accumulateOrCreateContinuousQueuedReplayableEvent(
queuedDrag,
blockedOn,
domEventName,
eventSystemFlags,
targetContainer,
dragEvent,
);
return true;
}
case 'mouseover': {
const mouseEvent = ((nativeEvent: any): MouseEvent);
// 合并或创建可重放事件
queuedMouse = accumulateOrCreateContinuousQueuedReplayableEvent(
queuedMouse,
blockedOn,
domEventName,
eventSystemFlags,
targetContainer,
mouseEvent,
);
return true;
}
case 'pointerover': {
const pointerEvent = ((nativeEvent: any): PointerEvent);
const pointerId = pointerEvent.pointerId;
queuedPointers.set(
pointerId,
// 合并或创建可重放事件
accumulateOrCreateContinuousQueuedReplayableEvent(
queuedPointers.get(pointerId) || null,
blockedOn,
domEventName,
eventSystemFlags,
targetContainer,
pointerEvent,
),
);
return true;
}
case 'gotpointercapture': {
const pointerEvent = ((nativeEvent: any): PointerEvent);
const pointerId = pointerEvent.pointerId;
queuedPointerCaptures.set(
pointerId,
// 合并或创建可重放事件
accumulateOrCreateContinuousQueuedReplayableEvent(
queuedPointerCaptures.get(pointerId) || null,
blockedOn,
domEventName,
eventSystemFlags,
targetContainer,
pointerEvent,
),
);
return true;
}
}
// 未加入队列,返回false
return false;
}
工具函数之 accumulateOrCreateContinuousQueuedReplayableEvent
accumulateOrCreateContinuousQueuedReplayableEvent
是 React 事件系统中用于 合并或创建可重放事件 的关键函数,主要解决 Suspense 阻塞期间连续触发的事件 的高效处理问题。
function accumulateOrCreateContinuousQueuedReplayableEvent(
existingQueuedEvent: null | QueuedReplayableEvent,// 已存在的事件对象
blockedOn: null | Container | SuspenseInstance,// 阻塞目标(Suspense 或容器)
domEventName: DOMEventName,// 事件类型(如 'click')
eventSystemFlags: EventSystemFlags, // 事件标志位
targetContainer: EventTarget,// 事件目标容器
nativeEvent: AnyNativeEvent,// 原生事件对象
): QueuedReplayableEvent {
// 如果事件是首次触发,创建一个新的 QueuedReplayableEvent。
if (
existingQueuedEvent === null ||
existingQueuedEvent.nativeEvent !== nativeEvent
) {
// 创建事件
const queuedEvent = createQueuedReplayableEvent(
blockedOn,
domEventName,
eventSystemFlags,
targetContainer,
nativeEvent,
);
// 阻塞源
if (blockedOn !== null) {
const fiber = getInstanceFromNode(blockedOn);
if (fiber !== null) {
// 服务端渲染
// attemptContinuousHydration(fiber);
}
}
return queuedEvent;
}
// 如果事件已存在(如连续触发),合并标志位和容器信息,避免重复创建。
existingQueuedEvent.eventSystemFlags |= eventSystemFlags;
const targetContainers = existingQueuedEvent.targetContainers;
// 如果目标容器不在现有列表中,将其加入(支持多容器场景,如微前端)
if (
targetContainer !== null &&
targetContainers.indexOf(targetContainer) === -1
) {
targetContainers.push(targetContainer);
}
return existingQueuedEvent;
}
工具函数之 createQueuedReplayableEvent
createQueuedReplayableEvent
是 React 事件系统中的一个工厂函数,用于创建一个 可重放的事件对象,主要用于处理因 Suspense 阻塞 或 并发渲染中断 而暂时无法立即处理的事件。
function createQueuedReplayableEvent(
blockedOn: null | Container | SuspenseInstance,
domEventName: DOMEventName,
eventSystemFlags: EventSystemFlags,
targetContainer: EventTarget,
nativeEvent: AnyNativeEvent,
): QueuedReplayableEvent {
return {
blockedOn,
domEventName,
eventSystemFlags,
nativeEvent,
targetContainers: [targetContainer],
};
}
{
blockedOn, // 事件被阻塞的原因(Suspense 实例或容器)
domEventName, // 事件类型(如 'click'、'mouseover')
eventSystemFlags, // 事件系统内部标志(如是否捕获阶段)
nativeEvent, // 原生浏览器事件对象
targetContainers: [targetContainer], // 事件目标容器数组
}
工具函数之 getInstanceFromNode
getInstanceFromNode
是 React 内部用于 从 DOM 节点直接获取关联的 Fiber 实例 的辅助函数。
function getInstanceFromNode(node: Node): Fiber | null {
const inst =
// 普通 React DOM 节点
(node: any)[internalInstanceKey] ||
// Portal 容器节点
(node: any)[internalContainerInstanceKey];
if (inst) {
const tag = inst.tag;
if (
tag === HostComponent ||
tag === HostText ||
tag === SuspenseComponent ||
tag === HostHoistable ||
tag === HostSingleton ||
tag === HostRoot
) {
return inst;
} else {
return null;
}
}
return null;
}
processDispatchQueue
processDispatchQueue
是 React 事件系统中用于 批量处理事件队列 的核心函数。
function processDispatchQueue(
dispatchQueue: DispatchQueue,
eventSystemFlags: EventSystemFlags,
): void {
// 判断当前事件阶段(捕获或冒泡)
const inCapturePhase = (eventSystemFlags & IS_CAPTURE_PHASE) !== 0;
// 遍历事件队列
for (let i = 0; i < dispatchQueue.length; i++) {
// event合成事件对象
// listeners 事件监听器列表
const {event, listeners} = dispatchQueue[i];
// 调用 processDispatchQueueItemsInOrder 处理单个事件的监听器
processDispatchQueueItemsInOrder(event, listeners, inCapturePhase);
}
type DispatchEntry = {
event: ReactSyntheticEvent,
listeners: Array<DispatchListener>,
};
export type DispatchQueue = Array<DispatchEntry>;
type DispatchListener = {
instance: null | Fiber,
listener: Function,
currentTarget: EventTarget,
};
工具函数之 processDispatchQueueItemsInOrder
processDispatchQueueItemsInOrder
是 React 事件系统中用于 按序执行事件监听器 的核心函数。
function processDispatchQueueItemsInOrder(
event: ReactSyntheticEvent,
dispatchListeners: Array<DispatchListener>,
inCapturePhase: boolean,
): void {
let previousInstance;
// 捕获阶段
if (inCapturePhase) {
// 从后往前遍历
for (let i = dispatchListeners.length - 1; i >= 0; i--) {
const {instance, currentTarget, listener} = dispatchListeners[i];
// 检查是否提前终止。(若 instance 变化(切换到新组件)、事件传播已被阻止)
if (instance !== previousInstance && event.isPropagationStopped()) {
return;
}
// 调用 executeDispatch 执行事件处理
executeDispatch(event, listener, currentTarget);
// 更新 previousInstance 记录当前实例
previousInstance = instance;
}
// 冒泡阶段
} else {
// 从前往后遍历
for (let i = 0; i < dispatchListeners.length; i++) {
const {instance, currentTarget, listener} = dispatchListeners[i];
// 检查是否提前终止
if (instance !== previousInstance && event.isPropagationStopped()) {
return;
}
executeDispatch(event, listener, currentTarget);
previousInstance = instance;
}
}
}
executeDispatch
executeDispatch
函数是 React 中用于执行事件监听器的工具函数。它的主要作用是将事件对象、事件监听器和当前事件目标关联起来,然后调用事件监听器处理事件,并在出现异常时进行全局错误报告。
函数参数含义:
event
: 这是一个ReactSyntheticEvent
类型的对象,它是 React 对原生 DOM 事件进行封装后的合成事件对象。该对象包含了事件的相关信息,如事件类型、事件触发的位置等。listener
: 是一个函数,它是在事件触发时需要执行的事件监听器。这个函数通常是开发者在组件中定义的,用于处理特定的事件。currentTarget
: 表示当前事件的目标元素,即绑定了事件监听器的元素。
function executeDispatch(
event: ReactSyntheticEvent,
listener: Function,
currentTarget: EventTarget,
): void {
event.currentTarget = currentTarget;
// 调用事件监听器并处理异常
try {
listener(event);
} catch (error) {
reportGlobalError(error);
}
// 在事件监听器执行完毕后,将 event 对象的 currentTarget 属性重置为 null。这是为了避免事件对象在后续的使用中保留不必要的引用,防止内存泄漏和潜在的错误。
event.currentTarget = null;
}