React19源码系列之 事件优先级

发布于:2025-06-11 ⋅ 阅读:(25) ⋅ 点赞:(0)

事件优先级

React 的合成事件系统与事件优先级机制 共同确保了用户交互的即时响应和复杂渲染任务的高效调度。

事件优先级

特点

离散事件(Discrete Events)

clickkeydownfocus

优先级最高,立即处理,不会被中断

连续事件(Continuous Events)

scrolldragtouchmove

优先级中等,允许被更高优先级中断

默认事件(Default Events)

useEffect、状态更新回调

优先级较低,可被用户交互中断

空闲事件(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 内部用于在事件处理流程中 定位阻塞渲染的实例 的核心函数。

其核心作用是:

  1. 通过事件目标(nativeEventTarget)找到对应的 Fiber 实例(如 ContainerSuspenseInstance
  2. 识别哪些组件实例会阻塞事件的传播或渲染(例如 Suspense 边界、根容器)
function findInstanceBlockingEvent(
  nativeEvent: AnyNativeEvent,
): null | Container | SuspenseInstance {
  const nativeEventTarget = getEventTarget(nativeEvent);
  return findInstanceBlockingTarget(nativeEventTarget);
}

工具函数之 getEventTarget

getEventTarget 是 React 内部用于 规范化事件目标(Event Target) 的工具函数。它从原生事件对象中提取实际触发事件的 DOM 节点,并处理以下边缘情况:

  1. 跨浏览器兼容性:兼容不同浏览器对事件目标的不同实现(如 targetsrcElement
  2. SVG 使用元素处理:处理 SVG 中 <use> 元素的特殊情况
  3. 文本节点归一化:将文本节点(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 节点 的核心函数。其核心作用是:

  1. 处理未挂载或正在挂载的节点:通过向上遍历,找到最近的已挂载父节点
  2. 识别已卸载的组件树:若整棵树已卸载,返回 null
  3. 支持并发渲染:正确处理插入(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 事件系统中用于 清理连续事件队列 的辅助函数,主要解决 某些特殊事件类型(如 focusdragmouse 等)在快速连续触发时可能导致的逻辑冲突问题

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 事件系统中用于 管理连续事件队列 的核心函数,专门处理那些需要 特殊跟踪的持续性事件(如 focusdragmousepointer 事件)。它的核心作用是将这些事件加入对应的全局队列。

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;
}