React19源码系列之 事件优先级
事件优先级
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/srcElementlet 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 边界的 Fiberif (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 减 1depth--;}// 处理结束标记} 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 或 Hydratingif ((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 节点),说明组件树已卸载,返回 nullreturn 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表示根fiberif (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 为 parentNodeif (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_eventcase '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_eventcase '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,);// 成功加入队列,返回truereturn 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;}}// 未加入队列,返回falsereturn 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;
}