Skip to content

React 事件处理机制:从事件提取到分发的完整流程

在 React 的事件系统中,从原生 DOM 事件触发到最终执行组件中的事件处理函数,需要经历两个关键阶段:事件提取(Event Extraction)事件分发(Event Dispatch)。这两个阶段共同构成了 React 事件处理的核心机制,确保了跨浏览器的一致性和高效的事件管理。

事件提取机制

当一个原生 DOM 事件被触发并由 React 的根监听器捕获后,并不会立即将这个原生事件直接派发给组件。相反,它会进入一个"事件提取"阶段,这个阶段的核心职责是将原生 DOM 事件转换为 React 的合成事件 (SyntheticEvent),并收集与该事件相关的监听器。

顶层 extractEvents 函数

react/packages/react-dom-bindings/src/events/DOMPluginEventSystem.js 文件中,有一个名为 extractEvents 函数,它的职责是按顺序调用注册在系统中的各个事件插件的 extractEvents 方法。

extractEvents 函数
javascript
// ... existing code ...
function extractEvents(
  dispatchQueue: DispatchQueue, // 用于收集事件和监听器的队列
  domEventName: DOMEventName,    // 触发的原生 DOM 事件名 (如 'click')
  targetInst: null | Fiber,      // 与原生事件目标最近的 Fiber 实例
  nativeEvent: AnyNativeEvent,   // 原生浏览器事件对象
  nativeEventTarget: null | EventTarget, // 原生事件的目标 DOM 元素
  eventSystemFlags: EventSystemFlags, // 事件系统标志 (如是否捕获阶段)
  targetContainer: EventTarget,  // 事件发生的根容器
) {
  // 1. 首先调用 SimpleEventPlugin 的 extractEvents
  // SimpleEventPlugin 处理大部分简单事件的映射和分发逻辑
  SimpleEventPlugin.extractEvents(
    dispatchQueue,
    domEventName,
    targetInst,
    nativeEvent,
    nativeEventTarget,
    eventSystemFlags,
    targetContainer,
  );

  // 2. 根据 eventSystemFlags 判断是否需要处理其他"polyfill"性质的插件
  const shouldProcessPolyfillPlugins =
    (eventSystemFlags & SHOULD_NOT_PROCESS_POLYFILL_EVENT_PLUGINS) === 0;

  // 通常情况下,这些插件只在原生事件的"冒泡"阶段被处理,
  // 因为 React 仍然在内部模拟捕获阶段,直接在捕获阶段调用这些插件可能会导致状态问题。
  if (shouldProcessPolyfillPlugins) {
    // 3. 依次调用其他插件的 extractEvents 方法
    EnterLeaveEventPlugin.extractEvents(...);
    ChangeEventPlugin.extractEvents(...);
    SelectEventPlugin.extractEvents(...);
    BeforeInputEventPlugin.extractEvents(...);
    FormActionEventPlugin.extractEvents(...); // 如果启用了相关特性
    // ScrollEndEventPlugin.extractEvents(...); // 如果启用了相关特性
  }
}
// ... existing code ...

事件插件内部的 extractEvents 方法

每个事件插件(如 SimpleEventPlugin, ChangeEventPlugin, EnterLeaveEventPlugin 等)都必须实现一个 extractEvents 方法。这个方法是插件的核心,它接收与顶层 extractEvents 函数类似的参数。

其主要职责包括(以 SimpleEventPlugin 为例):

  • 映射原生事件到 React 事件名:将如 click 映射到 onClick
  • 选择合适的合成事件构造函数:根据原生事件类型(如 MouseEvent, KeyboardEvent)选择对应的 SyntheticMouseEvent, SyntheticKeyboardEvent
  • 处理特定事件的浏览器兼容性问题:例如,过滤掉 Firefox 中由鼠标右键触发的 click 事件,或处理 focusin/focusout 到 onFocus/onBlur 的转换
  • 收集监听器:调用 accumulateSinglePhaseListeners(或在特定情况下调用 accumulateEventHandleNonManagedNodeListeners)来遍历 Fiber 树,收集捕获和冒泡阶段的事件处理函数
  • 创建合成事件对象:如果收集到监听器,则创建一个合成事件实例
  • 推入调度队列:将合成事件和监听器列表推入 dispatchQueue

📱 想了解更多 React 事件处理机制?

本文只是 React 事件处理机制深度解析的一部分内容。想要获取完整的技术文章和更多 React 源码解析内容,请关注我的微信公众号!

🔥 关注公众号获取:

  • 完整的 React 事件分发流程解析
  • React 合成事件的底层实现
  • 事件插件系统的设计原理
  • React 性能优化最佳实践
  • 前端架构设计思路
  • 最新技术趋势分析

📲 扫码关注

微信公众号

公众号:前端小卒

专注于前端技术的深度剖析,带你从源码层面理解 React、Vue 等主流框架的设计思想和实现原理。每周更新,干货满满!


本文为预览版本,完整内容请关注公众号获取。