React19源码系列之 事件插件系统

news2025/6/12 22:56:36

事件类别

事件类型

定义

文档

Event

Event 接口表示在 EventTarget 上出现的事件。

Event - Web API | MDN

UIEvent

UIEvent 接口表示简单的用户界面事件。

UIEvent - Web API | MDN

KeyboardEvent

KeyboardEvent 对象描述了用户与键盘的交互。

KeyboardEvent - Web API | MDN

MouseEvent

MouseEvent 接口指用户与指针设备(如鼠标)交互时发生的事件。

鼠标事件 - Web API | MDN

PointerEvent

PointerEvent 接口代表了由 指针 引发的 DOM 事件的状态,包括接触点的位置,引发事件的设备类型,接触表面受到的压力等。

PointerEvent - Web API | MDN

FocusEvent

FocusEvent 接口表示和焦点相关的事件比如 focus, blur, focusin, 和 focusout。

FocusEvent - Web API | MDN

TouchEvent

TouchEvent 接口表示一种 UIEvent,当具有触摸表面的接触状态改变时会发送该事件。

TouchEvent - Web API | MDN

DragEvent

DragEvent 是一个表示拖、放交互的一个DOM event 接口。

DragEvent - Web API | MDN

WheelEvent

WheelEvent 接口表示用户滚动鼠标滚轮或类似的输入设备时触发的事件。

WheelEvent - Web API | MDN

ToggleEvent

ToggleEvent 接口表示当元素的状态发生改变时通知用户的事件。

ToggleEvent - Web API | MDN

TransitonEvent

TransitonEvent 接口指那些提供了与过渡有关信息的事件。

TransitionEvent - Web API | MDN

InputEvent

InputEvent 接口表示通知用户可编辑内容更改的事件。

InputEvent - Web API | MDN

CompositionEvent

DOM 接口 CompositionEvent 表示用户间接输入文本(如使用输入法)时发生的事件。

CompositionEvent - Web API | MDN

AnimationEvent

The AnimationEvent interface represents events providing information related to animations.

AnimationEvent - Web APIs | MDN

ClipboardEvent

ClipboardEvent 接口描述了与修改剪切板相关信息的事件,这些事件包括 剪切cut , 复制copy 和 粘贴paste 事件。

ClipboardEvent - Web API | MDN

 

1、SyntheticEvent(基础事件)

SyntheticEvent 是 React 基于 createSyntheticEvent 工厂函数创建的 合成事件基类

const SyntheticEvent = createSyntheticEvent(EventInterface);

EventInterface 是 React 合成事件系统的基础配置对象,定义了 如何从原生 DOM 事件中提取标准属性 到 React 的合成事件对象。 

// EventInterface 是 React 合成事件系统的基础配置对象,定义了 如何从原生 DOM 事件中提取标准属性 到 React 的合成事件对象。
const EventInterface = {
  eventPhase: 0,// 表示事件流当前处于哪一个阶段。
  bubbles: 0, // Event 接口的 bubbles 只读属性表明事件是否会沿 DOM 树向上冒泡。
  cancelable: 0, // Event 实例的只读属性 cancelable 表明该事件是否可以被取消,即事件是否可以像从未发生一样被阻止
  timeStamp: function (event: {[propName: string]: mixed}) {
    // Event 接口的 timeStamp 只读属性返回事件创建的时间(以毫秒为单位)。
    return event.timeStamp || Date.now();
  },
  defaultPrevented: 0,// 布尔值,表明当前事件是否调用了 event.preventDefault()方法。
  isTrusted: 0, // Event 接口的 isTrusted 只读属性是一个表示事件是否由用户行为生成的布尔值。当事件由用户行为触发时,为 true;当事件由脚本创建或修改,或通过 EventTarget.dispatchEvent() 派发时,为 false。
};

工具函数之 createSyntheticEvent

createSyntheticEvent 是 React 合成事件系统的核心工厂函数,用于创建 跨浏览器兼容的合成事件对象。它封装了原生 DOM 事件,提供统一的 API 并处理浏览器差异。

function createSyntheticEvent(Interface: EventInterfaceType) {

  // SyntheticBaseEvent 构造函数
  function SyntheticBaseEvent(
    reactName: string | null,// React 事件名称。
    reactEventType: string,//React 事件类型
    targetInst: Fiber | null,// 事件目标对应的 Fiber 实例。
    nativeEvent: {[propName: string]: mixed, ...},// 原生 DOM 事件对象。
    nativeEventTarget: null | EventTarget,// 原生事件的目标元素。
  ) {
    // _reactName:React 事件处理器属性名(如 'onClick')。
    this._reactName = reactName;

    // _targetInst:事件目标对应的 Fiber 节点(用于事件冒泡)。
    this._targetInst = targetInst;
    this.type = reactEventType;
    this.nativeEvent = nativeEvent;
    this.target = nativeEventTarget;
    this.currentTarget = null;

    // 根据 Interface 提取并标准化属性
    for (const propName in Interface) {
      if (!Interface.hasOwnProperty(propName)) {
        continue;
      }
      const normalize = Interface[propName];
      if (normalize) {
        this[propName] = normalize(nativeEvent);
      } else {
        this[propName] = nativeEvent[propName];
      }
    }

     // 处理默认阻止行为
    const defaultPrevented =
      nativeEvent.defaultPrevented != null
        ? nativeEvent.defaultPrevented
        : nativeEvent.returnValue === false;
    
    if (defaultPrevented) {
      this.isDefaultPrevented = functionThatReturnsTrue;
    } else {
      this.isDefaultPrevented = functionThatReturnsFalse;
    }
    
    this.isPropagationStopped = functionThatReturnsFalse;
    return this;
  }

  // 为 SyntheticBaseEvent 的原型添加方法
  assign(SyntheticBaseEvent.prototype, {

    阻止默认行为,并更新状态标志。
    preventDefault: function () {
      this.defaultPrevented = true;
      const event = this.nativeEvent;
      if (!event) {
        return;
      }

      if (event.preventDefault) {
        event.preventDefault();// 标准浏览器

      } else if (typeof event.returnValue !== 'unknown') {
        // Event 接口的 returnValue 属性设置为 false 即可阻止默认操作。
        event.returnValue = false;
      }
      this.isDefaultPrevented = functionThatReturnsTrue;
    },

    // 停止事件冒泡,并更新状态标志。
    stopPropagation: function () {
      const event = this.nativeEvent;
      if (!event) {
        return;
      }

      if (event.stopPropagation) {
        event.stopPropagation();// 标准浏览器
       
      } else if (typeof event.cancelBubble !== 'unknown') 
        // Event 接口的 cancelBubble 属性值为 true 表示事件不得继续传播。
        event.cancelBubble = true;
      }

      this.isPropagationStopped = functionThatReturnsTrue;
    },

    // persist 方法:在现代事件系统中,不使用事件池,所以该方法为空。
    persist: function () {},
  
    // isPersistent 方法:始终返回 true。
    isPersistent: functionThatReturnsTrue,
  });
  
  return SyntheticBaseEvent;
}

type EventInterfaceType = {
  [propName: string]: 0 | ((event: {[propName: string]: mixed, ...}) => mixed),
};
function functionThatReturnsTrue() {
  return true;
}

function functionThatReturnsFalse() {
  return false;
}

2、SyntheticUIEvent(UI 原生事件)

SyntheticUIEvent 是 React 内部用于 处理 UI 相关原生事件 的合成事件类,主要解决 浏览器原生 UI 事件(如 focus、scroll、resize) 与 React 事件系统的集成问题。 

 const SyntheticUIEvent = createSyntheticEvent(UIEventInterface);
const UIEventInterface: EventInterfaceType = {
  ...EventInterface,
  view: 0, // UIEvent.view 只读属性返回 WindowProxy 生成事件的对象。
  detail: 0, // UIEvent.detail 是只读属性,当值为非空的时候,提供当前点击数 (和环境有关) 
};

3、SyntheticKeyboardEvent(键盘事件)

SyntheticKeyboardEvent 是 React 基于 createSyntheticEvent 工厂函数创建的 键盘事件专用合成事件类。【键盘事件】

const SyntheticKeyboardEvent = createSyntheticEvent(
  KeyboardEventInterface,
);

KeyboardEvent - Web API | MDN

const KeyboardEventInterface = {
  // 基础事件属性
  ...UIEventInterface,

  // 键盘特定属性
  key: getEventKey, // 按下的键(如 "Enter", "a")
  code: 0, // 物理键码(如 "KeyA", "Enter")
  location: 0,  // 键的位置(0=标准, 1=左, 2=右, 3=数字小键盘)
  ctrlKey: 0, // Ctrl 键是否按下
  shiftKey: 0, // Shift 键是否按下
  altKey: 0, // Alt 键是否按下
  metaKey: 0, // Meta 键(如 Windows 键)是否按下
  repeat: 0, // 是否为重复按键
  locale: 0,
  getModifierState: getEventModifierState,

  // 字符编码
  charCode: function (event: {[propName: string]: mixed}) {
    if (event.type === 'keypress') {
      // 从 KeyboardEvent 对象中提取字符编码。
      return getEventCharCode( event );
    }
    return 0;
  },

  // 键码
  keyCode: function (event: {[propName: string]: mixed}) {
    if (event.type === 'keydown' || event.type === 'keyup') {
      return event.keyCode;
    }
    return 0;
  },

  // 统一 keyCode 和 charCode
  which: function (event: {[propName: string]: mixed}) {
    if (event.type === 'keypress') {
      return getEventCharCode( event );
    }
    if (event.type === 'keydown' || event.type === 'keyup') {
      return event.keyCode;
    }
    return 0;
  },
};
const UIEventInterface: EventInterfaceType = {
  ...EventInterface,
  view: 0, // UIEvent.view 只读属性返回 WindowProxy 生成事件的对象。
  detail: 0, // UIEvent.detail 是只读属性,当值为非空的时候,提供当前点击数 (和环境有关) 
};

工具函数之 getEventModifierState 

function getEventModifierState(nativeEvent: {[propName: string]: mixed}) {
  return modifierStateGetter;
}

工具函数之 modifierStateGetter

modifierStateGetter 是 React 用于 查询键盘修饰键状态 的工具函数。

function modifierStateGetter(keyArg) {
  const syntheticEvent = this;
  // 从合成事件获取原生事件对象
  const nativeEvent = syntheticEvent.nativeEvent;
  // KeyboardEvent 对象上的 getModifierState() 方法。getModifierState() 方法允许你查询一个特定的修饰键是否被激活。
  if (nativeEvent.getModifierState) {
    return nativeEvent.getModifierState(keyArg);
  }
  // 过映射表(modifierKeyToProp)查找对应属性
  const keyProp = modifierKeyToProp[keyArg];
  return keyProp ? !!nativeEvent[keyProp] : false;
}
const modifierKeyToProp = {
  Alt: 'altKey',
  Control: 'ctrlKey',
  Meta: 'metaKey',
  Shift: 'shiftKey',
};

工具函数之 getEventCharCode

KeyboardEvent 对象中提取字符编码。由于不同浏览器在处理键盘事件时,对于字符编码的存储和返回方式存在差异,这个函数会对这些差异进行处理,最终返回一个有效的字符编码,如果是不可打印字符(除了回车键)则返回 0。

function getEventCharCode(nativeEvent: KeyboardEvent): number {
  // 声明变量 charCode 用于存储最终要返回的字符编码。
  let charCode;
  
  // 从 nativeEvent 中获取 keyCode,keyCode 是按键的代码,不同的按键有不同的 keyCode 值。
  const keyCode = nativeEvent.keyCode;

  // 检查 nativeEvent 对象是否包含 charCode 属性。
  if ('charCode' in nativeEvent) {
     // 如果包含,将 nativeEvent.charCode 的值赋给 charCode。
    charCode = nativeEvent.charCode;

    //对于 Firefox 浏览器,它在回车键按下时不会设置 charCode,所以当 charCode 为 0 且 keyCode 为 13(回车键的 keyCode)时,将 charCode 设置为 13。
    // FF does not set `charCode` for the Enter-key, check against `keyCode`.
    if (charCode === 0 && keyCode === 13) {
      charCode = 13;
    }
  } else {
    // 如果不包含,说明可能是 IE8 浏览器,它没有实现 charCode 属性,此时直接将 keyCode 的值赋给 charCode。
    // IE8 does not implement `charCode`, but `keyCode` has the correct value.
    charCode = keyCode;
  }

  // IE and Edge (on Windows) and Chrome / Safari (on Windows and Linux)
  // report Enter as charCode 10 when ctrl is pressed.
  // 在某些浏览器(如 Windows 上的 IE、Edge 以及 Windows 和 Linux 上的 Chrome、Safari)中,当按下 Ctrl 键和回车键时,charCode 会被报告为 10,这里将其修正为 13。
  if (charCode === 10) {
    charCode = 13;
  }

  // Some non-printable keys are reported in `charCode`/`keyCode`, discard them.
  // Must not discard the (non-)printable Enter-key.
  // 如果 charCode 大于等于 32 或者等于 13(回车键),说明是可打印字符或者回车键,返回该 charCode;
  if (charCode >= 32 || charCode === 13) {
    return charCode;
  }
// 否则,认为是不可打印字符,返回 0。
  return 0;
}

 4、SyntheticFocusEvent(焦点事件)

SyntheticFocusEvent 是 React 基于 createSyntheticEvent 工厂函数创建的 焦点事件专用合成事件类

const SyntheticFocusEvent =  createSyntheticEvent(FocusEventInterface);
const FocusEventInterface: EventInterfaceType = {
  // 基础事件属性
  ...UIEventInterface,
  
  relatedTarget: 0, // 失去/获得焦点时的相关元素
};

 5、SyntheticMouseEvent(鼠标事件)

SyntheticMouseEvent 是 React 基于 createSyntheticEvent 工厂函数创建的 鼠标事件专用合成事件类

const SyntheticMouseEvent = createSyntheticEvent(MouseEventInterface);
const MouseEventInterface: EventInterfaceType = {
  ...UIEventInterface,
  
  screenX: 0, // 屏幕坐标 X
  screenY: 0, // 屏幕坐标 Y
  clientX: 0,  // 客户端坐标 X
  clientY: 0, // 客户端坐标 Y
  pageX: 0,   // 页面坐标 X
  pageY: 0,   // 页面坐标 Y
  
  ctrlKey: 0,  // Ctrl 键是否按下
  shiftKey: 0,  // Shift 键是否按下
  altKey: 0,  // Alt 键是否按下
  metaKey: 0,  // Meta 键是否按下
  
  getModifierState: getEventModifierState,
  button: 0,  // 按下的按钮(0=主按钮,1=中键,2=辅助按钮)
  buttons: 0,   // 当前按下的按钮掩码
  
  relatedTarget: function (event) {
    // 旧版浏览器(如 IE)使用 fromElement/toElement
    if (event.relatedTarget === undefined)
      return event.fromElement === event.srcElement
        ? event.toElement // mouseleave 场景
        : event.fromElement; // mouseenter 场景

    // 现代浏览器直接返回 relatedTarget
    return event.relatedTarget;
  },

  // 相对上次x坐标的位移
  movementX: function (event) {
    if ('movementX' in event) {
      return event.movementX;
    }
    updateMouseMovementPolyfillState(event);
    return lastMovementX;
  },

  // 相对于上次坐标y轴位移
  movementY: function (event) {
    if ('movementY' in event) {
      return event.movementY;
    }
    return lastMovementY;
  },
};

MouseEvent 接口的 movementX 只读属性提供了给定事件与前一个 mousemove 事件之间鼠标指针在 X 坐标轴上的移动值。换句话说,该属性的值计算如下:currentEvent.movementX = currentEvent.screenX - previousEvent.screenX

MouseEvent 接口的 movementY 只读属性提供了当前事件和上一个 mousemove 事件之间鼠标指针在 Y 坐标轴上的移动值。换句话说,这个值是这样计算的:currentEvent.movementY = currentEvent.screenY - previousEvent.screenY

工具函数之 updateMouseMovementPolyfillState

updateMouseMovementPolyfillState 是 React 为 不支持原生 movementX/Y 属性的浏览器 提供的位移计算函数。


let lastMovementX;
let lastMovementY;
let lastMouseEvent;

function updateMouseMovementPolyfillState(event: {[propName: string]: mixed}) {
  // 检查当前事件是否与上一次事件相同。若相同则跳过计算(避免重复处理)
  if (event !== lastMouseEvent) {

    // 若存在上一次事件且当前为 mousemove 事件
    if (lastMouseEvent && event.type === 'mousemove') {
      lastMovementX = event.screenX - lastMouseEvent.screenX;
      lastMovementY = event.screenY - lastMouseEvent.screenY;
    } else {
      // 重置位移
      lastMovementX = 0;
      lastMovementY = 0;
    }
    
    // 保存当前事件引用
    lastMouseEvent = event;
  }
}

 6、SyntheticDragEvent(拖拽事件)

SyntheticDragEvent 是 React 基于 createSyntheticEvent 工厂函数创建的 拖拽事件专用合成事件类

const SyntheticDragEvent = createSyntheticEvent(DragEventInterface);
const DragEventInterface: EventInterfaceType = {
  ...MouseEventInterface,
  
  dataTransfer: 0,// 拖拽数据对象(包含拖拽内容、类型等)
};

7、SyntheticPointerEvent(指针事件)

Pointer Events

SyntheticPointerEvent 是 React 基于 createSyntheticEvent 工厂函数创建的 指针事件专用合成事件类


const SyntheticPointerEvent: $FlowFixMe = createSyntheticEvent(
 PointerEventInterface,
);
const PointerEventInterface = {
  ...MouseEventInterface,
  
  pointerId: 0, // 指针唯一标识符
  width: 0, // 接触面积的宽度(像素)
  height: 0, // 接触面积的高度(像素)
  pressure: 0, // 压力值(0-1,鼠标通常为 0.5)
  tangentialPressure: 0, // 压力值
  tiltX: 0, // 指针在 X 轴的倾斜角度
  tiltY: 0, // 指针相对于视口的 Y 坐标
  twist: 0, // 传感器(笔)顺时针旋转角度
  pointerType: 0, // 指针类型('mouse'/'touch'/'pen')
  isPrimary: 0,  // 是否为主指针(多点触控时)
};

8、SyntheticTouchEvent(触摸事件)

SyntheticTouchEvent 是 React 基于 createSyntheticEvent 工厂函数创建的 触摸事件专用合成事件类

const SyntheticTouchEvent = createSyntheticEvent(TouchEventInterface);
const TouchEventInterface = {
  ...UIEventInterface,

  // 触摸特定属性
  touches: 0, // 当前屏幕上所有触摸点的列表
  targetTouches: 0, // 与当前目标元素相关的触摸点列表
  changedTouches: 0, // 与当前事件相关的触摸点列表
  altKey: 0, // Alt 键是否按下
  metaKey: 0,  // Meta 键是否按下
  ctrlKey: 0, // Ctrl 键是否按下
  shiftKey: 0,   // Shift 键是否按下
  getModifierState: getEventModifierState,
};

9、SyntheticTransitionEvent(css 过渡事件)

CSS Transitions

SyntheticTransitionEvent 是 React 基于 createSyntheticEvent 工厂函数创建的 CSS 过渡事件专用合成事件类

const SyntheticTransitionEvent = createSyntheticEvent(TransitionEventInterface);
const TransitionEventInterface = {
  ...EventInterface,
  propertyName: 0,  // 发生过渡的 CSS 属性名称
  elapsedTime: 0,  // 过渡已运行的时间(秒)
  pseudoElement: 0,  // 过渡发生的伪元素(如 '::before')
};

10、SyntheticWheelEvent(滚轮事件)

UI Events

SyntheticWheelEvent 是 React 基于 createSyntheticEvent 工厂函数创建的 滚轮事件专用合成事件类

const SyntheticWheelEvent = createSyntheticEvent(WheelEventInterface);
const WheelEventInterface = {
  ...MouseEventInterface,

  // 水平滚动量
  deltaX(event: {[propName: string]: mixed}) {
    return 'deltaX' in event
      // WheelEvent.deltaX 只读属性是一个表示以 WheelEvent.deltaMode 为单位的水平滚动量的 double 值。
      ? event.deltaX
      : 
        'wheelDeltaX' in event
        ? 
          -event.wheelDeltaX
        : 0;
  },
  // 垂直滚动量
  deltaY(event: {[propName: string]: mixed}) {
    return 'deltaY' in event
      ? event.deltaY
      : 
        'wheelDeltaY' in event
        ? 
          -event.wheelDeltaY
        : 
          'wheelDelta' in event
          ? 
            -event.wheelDelta
          : 0;
  },
   // 深度滚动量(极少使用)
  deltaZ: 0,
  // 滚动单位(0=像素,1=行,2=页)
  // WheelEvent.deltaMode 只读属性返回 unsigned long,表示滚动量的 delta 值的单位。
  deltaMode: 0,
};

11、SyntheticToggleEvent(切换事件)

SyntheticToggleEvent 是 React 基于 createSyntheticEvent 工厂函数创建的 切换事件专用合成事件类

const SyntheticToggleEvent = createSyntheticEvent(ToggleEventInterface);
const ToggleEventInterface = {
  ...EventInterface,
  newState: 0, // 新状态
  oldState: 0, // 旧状态
};

12、SyntheticCompositionEvent(组合事件)

SyntheticCompositionEvent 是 React 内部用于 处理输入法组合文本事件 的合成事件类,主要解决 非直接文本输入(如拼音、日文假名输入) 的捕获和处理问题。

const SyntheticCompositionEvent: $FlowFixMe = createSyntheticEvent(
  CompositionEventInterface,
);
const CompositionEventInterface: EventInterfaceType = {
  ...EventInterface,
  data: 0, // 组合文本内容
};

13、SyntheticInputEvent(输入事件)

React 事件系统中一个有趣的设计:将合成输入事件(SyntheticInputEvent)与合成组合事件(SyntheticCompositionEvent)复用同一实现

const SyntheticInputEvent = SyntheticCompositionEvent;

14、SyntheticAnimationEvent(动画事件)

React 中创建 合成动画事件(Synthetic Animation Event) 的核心逻辑,主要通过 createSyntheticEvent 工厂函数生成统一的动画事件抽象。

const SyntheticAnimationEvent: $FlowFixMe = createSyntheticEvent(
  AnimationEventInterface,
);
const AnimationEventInterface: EventInterfaceType = {
  ...EventInterface,
  // animationName:触发动画的 CSS 动画名称。
  animationName: 0,
  // elapsedTime:动画已经运行的时间(秒)。
  elapsedTime: 0,
  // pseudoElement:动画应用的伪元素(如 ::before、::after)。
  pseudoElement: 0,
};

15、SyntheticClipboardEvent(剪贴板事件)

React 中创建 合成剪贴板事件(Synthetic Clipboard Event) 的核心逻辑,通过 createSyntheticEvent 工厂函数生成统一的剪贴板事件抽象,用于处理剪切、复制、粘贴等操作。

const SyntheticClipboardEvent: $FlowFixMe = createSyntheticEvent(
  ClipboardEventInterface,
);
const ClipboardEventInterface: EventInterfaceType = {
  ...EventInterface,
  // 用于访问剪贴板内容,包含文本、文件等数据。
  clipboardData: function (event) {
    return 'clipboardData' in event
      ? event.clipboardData
      : window.clipboardData;
  },
};

 

事件插件系统

React 的事件系统通过 事件插件(Event Plugins) 实现了跨浏览器兼容、合成事件和事件委托等核心特性。这些插件是 React 事件系统的重要组成部分,负责处理不同类型的原生事件并将其转换为统一的合成事件。

事件插件系统

含义

SimpleEventPlugin

处理基本的 DOM 事件(如 clickfocusblur 等)。

EnterLeaveEventPlugin

处理鼠标进入 / 离开事件(mouseentermouseleave)。

ChangeEventPlugin

处理表单元素的变更事件(如 inputselecttextarea)。

SelectEventPlugin

处理文本选择事件(如 selectstartselectionchange)。

BeforeInputEventPlugin

处理输入前事件(beforeinput)。

FormActionEventPlugin

处理表单提交。

1、SimpleEventPlugin.extractEvents

extractEvents 函数是 React 事件系统中用于从原生 DOM 事件中提取相关信息,创建合成事件对象,并将合成事件和对应的事件监听器添加到调度队列中的核心函数。

它会根据不同的原生事件类型,选择合适的合成事件构造函数,还会根据事件系统标志和捕获阶段等条件,积累相应的事件监听器,最终将合成事件和监听器组合成一个对象添加到调度队列中。

函数参数含义:

  • domEventName:原生 DOM 事件的名称,如 'click''keydown' 等。
  • targetInst:事件目标对应的 Fiber 实例。
  • nativeEvent:原生的 DOM 事件对象。
  • nativeEventTarget:原生事件的目标元素。
  • eventSystemFlags:事件系统的标志,用于表示事件的状态和特性。
  • targetContainer:事件发生的目标容器。
function extractEvents(
  dispatchQueue: DispatchQueue,
  domEventName: DOMEventName,
  targetInst: null | Fiber,
  nativeEvent: AnyNativeEvent,
  nativeEventTarget: null | EventTarget,
  eventSystemFlags: EventSystemFlags,
  targetContainer: EventTarget,
): void {

  // 通过 topLevelEventsToReactNames 映射表获取原生 DOM 事件对应的 React 事件名称。
  const reactName = topLevelEventsToReactNames.get(domEventName);

  // 如果没有对应的 React 事件名称,则直接返回,不进行后续处理。
  if (reactName === undefined) {
    return;
  }

  // 初始化合成事件构造函数和事件类型
  let SyntheticEventCtor = SyntheticEvent;
  let reactEventType: string = domEventName;

  // 省略代码
   // switch(){}

  // 是否处于捕获节点
  const inCapturePhase = (eventSystemFlags & IS_CAPTURE_PHASE) !== 0;

  // 非托管节点,处理非 React 管理的 DOM 节点(如第三方库创建的节点)上的事件。
  if (
    enableCreateEventHandleAPI &&
    eventSystemFlags & IS_EVENT_HANDLE_NON_MANAGED_NODE
  ) {
    const listeners = accumulateEventHandleNonManagedNodeListeners(
      ((reactEventType: any): DOMEventName),
      targetContainer,
      inCapturePhase,
    );
    
    if (listeners.length > 0) {
      const event: ReactSyntheticEvent = new SyntheticEventCtor(
        reactName,
        reactEventType,
        null,
        nativeEvent,
        nativeEventTarget,
      );
      dispatchQueue.push({event, listeners});
    }
    
  } else {
    // react节点

    const accumulateTargetOnly =
      !inCapturePhase &&
      (domEventName === 'scroll' || domEventName === 'scrollend');

    const listeners = accumulateSinglePhaseListeners(
      targetInst,// 目标 Fiber
      reactName,// React 事件名(如 'onClick')
      nativeEvent.type,// 原生事件类型
      inCapturePhase,// 是否捕获阶段
      accumulateTargetOnly,// 是否仅目标节点
      nativeEvent,// 原生事件对象
    );
    
    if (listeners.length > 0) {
      const event: ReactSyntheticEvent = new SyntheticEventCtor(
        reactName,
        reactEventType,
        null,
        nativeEvent,
        nativeEventTarget,
      );
      dispatchQueue.push({event, listeners});
    }
  }

  
}
// 根据不同的原生事件类型选择合成事件构造函数
  switch (domEventName) {
      /** 键盘事件 */
    case 'keypress':
      if (getEventCharCode(((nativeEvent: any): KeyboardEvent)) === 0) {
        return;
      }
    case 'keydown':
    case 'keyup':
      SyntheticEventCtor = SyntheticKeyboardEvent;
      break;

   /** 焦点事件 */
    case 'focusin':
      // 浏览器特定事件名(如 focusin)转为 React 标准名(focus)。
      reactEventType = 'focus';
      SyntheticEventCtor = SyntheticFocusEvent;
      break;

   /** 失去焦点事件 */
    case 'focusout':
      reactEventType = 'blur';
      SyntheticEventCtor = SyntheticFocusEvent;
      break;

      /** 失去焦点前、失去焦点后 */
    case 'beforeblur':
    case 'afterblur':
      SyntheticEventCtor = SyntheticFocusEvent;
      break;
      
    case 'click':
      if (nativeEvent.button === 2) {
        return;
      }
      
    /* 鼠标事件 */
    case 'auxclick':
    case 'dblclick':
    case 'mousedown':
    case 'mousemove':
    case 'mouseup':
  
    case 'mouseout':
    case 'mouseover':
    case 'contextmenu':
      SyntheticEventCtor = SyntheticMouseEvent;
      break;

      /** 拖拽事件 */
    case 'drag':
    case 'dragend':
    case 'dragenter':
    case 'dragexit':
    case 'dragleave':
    case 'dragover':
    case 'dragstart':
    case 'drop':
      SyntheticEventCtor = SyntheticDragEvent;
      break;

        /** 触摸事件 */
    case 'touchcancel':
    case 'touchend':
    case 'touchmove':
    case 'touchstart':
      SyntheticEventCtor = SyntheticTouchEvent;
      break;

        /** 动画事件 */
    case ANIMATION_END:
    case ANIMATION_ITERATION:
    case ANIMATION_START:
      SyntheticEventCtor = SyntheticAnimationEvent;
      break;

        /** 转换事件 */
    case TRANSITION_END:
      SyntheticEventCtor = SyntheticTransitionEvent;
      break;

        /** 滚动事件 */
    case 'scroll':
    case 'scrollend':
      SyntheticEventCtor = SyntheticUIEvent;
      break;

        /** 滚轮事件 */
    case 'wheel':
      SyntheticEventCtor = SyntheticWheelEvent;
      break;

        /** 复制、粘贴、剪切事件 */
    case 'copy':
    case 'cut':
    case 'paste':
      SyntheticEventCtor = SyntheticClipboardEvent;
      break;

        /** 指针事件 */
    case 'gotpointercapture':
    case 'lostpointercapture':
    case 'pointercancel':
    case 'pointerdown':
    case 'pointermove':
    case 'pointerout':
    case 'pointerover':
    case 'pointerup':
      SyntheticEventCtor = SyntheticPointerEvent;
      break;

      /** 页面切换元素事件 */
    case 'toggle':
    case 'beforetoggle':
      SyntheticEventCtor = SyntheticToggleEvent;
      break;
      
    default:
      break;
  }

工具函数之 accumulateEventHandleNonManagedNodeListeners

用于收集非托管节点(non-managed node)事件监听器的核心函数。

accumulateEventHandleNonManagedNodeListeners 的主要作用是:

  • 从指定的 DOM 节点(currentTarget)上收集特定类型的事件监听器。
  • 这些监听器是直接绑定在 DOM 节点上的,而非通过 React 合成事件系统注册的(即 “非托管节点”)。
  • 收集后的监听器会被包装成 DispatchListener 对象。

函数参数含义:

  • reactEventType: React 内部事件名称(如 clickchange)。
  • currentTarget: 事件触发的 DOM 节点。
  • inCapturePhase: 是否处于事件捕获阶段(true 表示捕获阶段,false 表示冒泡阶段)。
function accumulateEventHandleNonManagedNodeListeners(
  reactEventType: DOMEventName,
  currentTarget: EventTarget,
  inCapturePhase: boolean,
): Array<DispatchListener> {

  // 初始化一个空数组,用于存储匹配的事件监听器
  const listeners: Array<DispatchListener> = [];

  // 从 currentTarget 节点获取所有事件监听器
  // getEventHandlerListeners 用于从 DOM 节点获取所有注册的事件监听器。这些监听器可能是通过 React 注册的,也可能是直接通过 addEventListener 注册的。
  const eventListeners = getEventHandlerListeners(currentTarget);

  // 如果该节点上有注册的事件监听器
  if (eventListeners !== null) {
    eventListeners.forEach(entry => {
      
       // 筛选出与当前事件类型和阶段匹配的监听器
      if (entry.type === reactEventType && entry.capture === inCapturePhase) {
        // 将符合条件的监听器包装成 DispatchListener 对象并添加到结果数组中
        listeners.push(
          // 使用 createDispatchListener 包装每个监听器
          createDispatchListener(null, entry.callback, currentTarget),
        );
      }
    });
  }
  return listeners;
}

工具函数之 getEventHandlerListeners

getEventHandlerListeners 是 React 内部用于 获取事件监听器集合 的工具函数。

React 在注册事件监听器时,会将监听器集合存储在作用域对象或 DOM 元素的 internalEventHandlerListenersKey 属性中。

function getEventHandlerListeners(
  scope: EventTarget | ReactScopeInstance,
): null | Set<ReactDOMEventHandleListener> {
  // 从 作用域对象(ReactScopeInstance)或 DOM 元素(EventTarget)中提取注册的事件监听器
  return (scope: any)[internalEventHandlerListenersKey] || null;
}

const internalEventHandlerListenersKey = '__reactListeners$' + randomKey;
type ReactDOMEventHandleListener = {
  callback: (SyntheticEvent<EventTarget>) => void, // 事件处理函数
  capture: boolean, // 是否为捕获阶段
  type: DOMEventName, // 事件类型(如 'click')
};

工具函数之 createDispatchListener

createDispatchListener 是 React 事件系统中用于 创建事件监听器包装对象 的工具函数。

function createDispatchListener(
  instance: null | Fiber, //事件监听器所在的 Fiber 节点(用于定位组件)
  listener: Function, // 实际的事件处理函数(如 onClick 回调)
  currentTarget: EventTarget, // 事件绑定的 DOM 元素(如 <button>)
): DispatchListener {
  return {
    instance,
    listener,
    currentTarget,
  };
}
// 返回值结构
{
  instance: Fiber,       // 组件对应的 Fiber 节点
  listener: Function,    // 事件处理函数
  currentTarget: Element // 事件当前目标 DOM 元素
}

工具函数之 accumulateSinglePhaseListeners

accumulateSinglePhaseListeners 是 React 事件系统中 负责收集事件监听器 的核心函数,它通过遍历 Fiber 树,从目标节点到根节点收集与当前事件相关的所有监听器(包括捕获和冒泡阶段)。

函数参数含义:

  • targetFiber: 事件触发的目标 Fiber 节点。
  • reactName: React 事件名(如 onClick)。
  • nativeEventType: 原生事件类型(如 click)。
  • inCapturePhase: 是否处于捕获阶段(true 为捕获,false 为冒泡)。
  • accumulateTargetOnly: 是否仅收集目标节点的监听器。
  • nativeEvent: 原生 DOM 事件对象。
function accumulateSinglePhaseListeners(
  targetFiber: Fiber | null,// 事件目标 Fiber 节点
  reactName: string | null,// React 事件名(如 'onClick')
  nativeEventType: string, // 原生事件类型(如 'click')
  inCapturePhase: boolean,// 是否处于捕获阶段
  accumulateTargetOnly: boolean,// 是否仅收集目标节点
  nativeEvent: AnyNativeEvent, // 原生事件对象
): Array<DispatchListener> {

  // 捕获阶段:事件名添加 Capture 后缀(如 onClickCapture)。
  const captureName = reactName !== null ? reactName + 'Capture' : null;
  // react事件名称,冒泡阶段:使用原始事件名(如 onClick)
  const reactEventName = inCapturePhase ? captureName : reactName;

  let listeners: Array<DispatchListener> = [];

  let instance = targetFiber;
  let lastHostComponent = null;

  // Accumulate all instances and listeners via the target -> root path.
// 遍历方向:从 targetFiber 开始,通过 instance.return 逐级向上访问父节点,直到根节点。
  while (instance !== null) {
    const {stateNode, tag} = instance;

    // HostComponent:处理真实 DOM 节点上的监听器。
    if (
      (tag === HostComponent ||
        tag === HostHoistable ||
        tag === HostSingleton) &&
      stateNode !== null
    ) {
      lastHostComponent = stateNode;

     // 1. 处理 createEventHandle 的监听器
      if (enableCreateEventHandleAPI) {
        const eventHandlerListeners =
          getEventHandlerListeners(lastHostComponent);
        
        if (eventHandlerListeners !== null) {
          eventHandlerListeners.forEach(entry => {
            if (
              entry.type === nativeEventType &&
              entry.capture === inCapturePhase
            ) {
              listeners.push(
                createDispatchListener(
                  instance,
                  entry.callback,
                  (lastHostComponent: any),
                ),
              );
            }
          });
        }
      }

       // 2. 处理标准 React 监听器(如 onClick)
      if (reactEventName !== null) {
        const listener = getListener(instance, reactEventName);
        if (listener != null) {
          listeners.push(
            createDispatchListener(instance, listener, lastHostComponent),
          );
        }
      }

      // 3. 处理 ScopeComponent 的监听器(实验性 API)
    } else if (
      enableCreateEventHandleAPI &&
      enableScopeAPI &&
      tag === ScopeComponent &&
      lastHostComponent !== null &&
      stateNode !== null
    ) {
      // Scopes
      // const reactScopeInstance = stateNode;
      // const eventHandlerListeners =
      //   getEventHandlerListeners(reactScopeInstance);
      
      // if (eventHandlerListeners !== null) {
      //   eventHandlerListeners.forEach(entry => {
      //     if (
      //       entry.type === nativeEventType &&
      //       entry.capture === inCapturePhase
      //     ) {
      //       listeners.push(
      //         createDispatchListener(
      //           instance,
      //           entry.callback,
      //           (lastHostComponent: any),
      //         ),
      //       );
      //     }
      //   });
      // }
    }

     // 如果仅需目标节点,提前终止遍历
    if (accumulateTargetOnly) {
      break;
    }

    // 处理焦点丢失事件的特殊逻辑,确保焦点正确转移。
    if (enableCreateEventHandleAPI && nativeEvent.type === 'beforeblur') {
    
      const detachedInterceptFiber = nativeEvent._detachedInterceptFiber;
      if (
        detachedInterceptFiber !== null &&
        (detachedInterceptFiber === instance ||
          detachedInterceptFiber === instance.alternate)
      ) {
        listeners = [];
      }
    }
    
    instance = instance.return;
  }

  return listeners;
}

2、EnterLeaveEventPlugin.extractEvents

extractEvents 是 React 事件系统中处理 鼠标 / 指针进入离开事件 的核心函数。

function extractEvents(
  dispatchQueue: DispatchQueue,
  domEventName: DOMEventName,
  targetInst: null | Fiber,
  nativeEvent: AnyNativeEvent,
  nativeEventTarget: null | EventTarget,
  eventSystemFlags: EventSystemFlags,
  targetContainer: EventTarget,
) {
  // 进入事件
  const isOverEvent =  domEventName === 'mouseover' || domEventName === 'pointerover';

  // 离开事件
  const isOutEvent =  domEventName === 'mouseout' || domEventName === 'pointerout';

  //  过滤无效事件
  if (isOverEvent && !isReplayingEvent(nativeEvent)) {

    // 目标元素
    const related =  (nativeEvent: any).relatedTarget || (nativeEvent: any).fromElement;
    
    if (related) {
     //  从 DOM 节点查找最近的 Fiber 实例 的核心函数
      if (
        getClosestInstanceFromNode(related) ||
        isContainerMarkedAsRoot(related)
      ) {
        return;
      }
    }
  }

  
  if (!isOutEvent && !isOverEvent) {
    // Must not be a mouse or pointer in or out - ignoring.
    return;
  }

  // 获取window对象
  let win;
  
  if ((nativeEventTarget: any).window === nativeEventTarget) {
    // `nativeEventTarget` is probably a window object.
    win = nativeEventTarget;
    
  } else {
    const doc = (nativeEventTarget: any).ownerDocument;
    
    if (doc) {
      win = doc.defaultView || doc.parentWindow;
      
    } else {
      win = window;
    }
  }

  // 当前正要离开的元素
  let from;
  // 要进入的元素
  let to;

  // 离开事件(mouseout/pointerout)
  if (isOutEvent) {
    // 获取目标元素
    const related = nativeEvent.relatedTarget || (nativeEvent: any).toElement;
    // 离开的元素(当前 Fiber 节点)
    from = targetInst;
    
    // 鼠标进入的元素(Fiber 节点),需通过 getClosestInstanceFromNode 从 DOM 节点获取对应的 Fiber。
    to = related ? getClosestInstanceFromNode((related: any)) : null;


    if (to !== null) {
      const nearestMounted = getNearestMountedFiber(to);
      const tag = to.tag;

         // 过滤未挂载或非宿主组件的节点
      if (
        to !== nearestMounted ||
        (tag !== HostComponent && tag !== HostSingleton && tag !== HostText)
      ) {
        to = null;
      }
    }
    
  } else {
    // Moving to a node from outside the window.
    from = null;
    to = targetInst;
  }

  if (from === to) {
    // Nothing pertains to our managed components.
    return;
  }

  /** 根据事件类型选择合成事件构造函数 */

  // 默认鼠标事件
  let SyntheticEventCtor = SyntheticMouseEvent;
  let leaveEventType = 'onMouseLeave';
  let enterEventType = 'onMouseEnter';
  let eventTypePrefix = 'mouse';

  // 指针事件
  if (domEventName === 'pointerout' || domEventName === 'pointerover') {
    SyntheticEventCtor = SyntheticPointerEvent;
    leaveEventType = 'onPointerLeave';
    enterEventType = 'onPointerEnter';
    eventTypePrefix = 'pointer';
  }

  /** 将 React Fiber 节点转换为实际 DOM 节点 */
  // 要离开的元素 
  const fromNode = from == null ? win : getNodeFromInstance(from);
  // 要进入的元素
  const toNode = to == null ? win : getNodeFromInstance(to);

  // 创建 leave 事件对象
  const leave: KnownReactSyntheticEvent = new SyntheticEventCtor(
    leaveEventType,  // 'onMouseLeave' 或 'onPointerLeave'
    eventTypePrefix + 'leave', // 'mouseleave' 或 'pointerleave'
    from,  // 源 Fiber 节点
    nativeEvent,  // 原生事件对象
    nativeEventTarget, // 原生事件目标
  );
  leave.target = fromNode; // 事件源 DOM 节点
  leave.relatedTarget = toNode; // 鼠标移向的节点


  // 创建 enter 事件对象
  let enter: KnownReactSyntheticEvent | null = null;


  const nativeTargetInst = getClosestInstanceFromNode((nativeEventTarget: any));
  // 仅当原生事件目标与当前处理的 Fiber 节点匹配时创建 enter 事件
  if (nativeTargetInst === targetInst) {
    
    const enterEvent: KnownReactSyntheticEvent = new SyntheticEventCtor(
      enterEventType,
      eventTypePrefix + 'enter',
      to,
      nativeEvent,
      nativeEventTarget,
    );

    // 设置事件对象属性
    enterEvent.target = toNode;
    enterEvent.relatedTarget = fromNode;
    
    enter = enterEvent;
  }

  // 构建事件派发队列
  accumulateEnterLeaveTwoPhaseListeners(dispatchQueue, leave, enter, from, to);
}

type BaseSyntheticEvent = {
  isPersistent: () => boolean,
  isPropagationStopped: () => boolean,
  _dispatchInstances?: null | Array<Fiber | null> | Fiber,
  _dispatchListeners?: null | Array<Function> | Function,
  _targetInst: Fiber,
  nativeEvent: Event,
  target?: mixed,
  relatedTarget?: mixed,
  type: string,
  currentTarget: null | EventTarget,
};

type KnownReactSyntheticEvent = BaseSyntheticEvent & {
  _reactName: string,
};

工具函数之 accumulateEnterLeaveTwoPhaseListeners

accumulateEnterLeaveTwoPhaseListeners 是 React 事件系统中处理 鼠标 / 指针进入离开事件 的核心函数。

函数参数含义:

  • dispatchQueue: 事件派发队列,用于存储待执行的事件监听器。
  • leaveEvent: 离开事件对象(如 onMouseLeave)。
  • enterEvent: 进入事件对象(如 onMouseEnter)。
  • from: 鼠标离开的元素对应的 Fiber 节点。
  • to: 鼠标进入的元素对应的 Fiber 节点。
function accumulateEnterLeaveTwoPhaseListeners(
  dispatchQueue: DispatchQueue, // 事件派发队列
  leaveEvent: KnownReactSyntheticEvent, //离开事件对象
  enterEvent: null | KnownReactSyntheticEvent, // 进入事件对象
  from: Fiber | null, //鼠标离开的元素对应的 Fiber
  to: Fiber | null, // 鼠标进入的元素对应的 Fiber
): void {

  // 寻找共同祖先节点
  const common = from && to ? getLowestCommonAncestor(from, to) : null;


  // 收集离开事件监听器(冒泡阶段)
  if (from !== null) {
    // 遍历方向:从 from 节点开始,向上遍历到 common 节点(不包含 common)。
    accumulateEnterLeaveListenersForEvent(
      dispatchQueue,
      leaveEvent,
      from,
      common,
      false, //  冒泡阶段(从内向外)
    );
  }
  
  // 收集进入事件监听器(捕获阶段)
  if (to !== null && enterEvent !== null) {
    // 遍历方向:从 common 节点(包含)开始,向下遍历到 to 节点。
    accumulateEnterLeaveListenersForEvent(
      dispatchQueue,
      enterEvent,
      to,
      common,
      true,  //  捕获阶段(从外向内)
    );
  }
}

工具函数之 getLowestCommonAncestor

getLowestCommonAncestor 是 React 内部用于 查找两个 Fiber 节点最近公共祖先 的核心算法。

function getLowestCommonAncestor(instA: Fiber, instB: Fiber): Fiber | null {
  let nodeA: null | Fiber = instA;
  let nodeB: null | Fiber = instB;

  /** 计算节点深度 */
  let depthA = 0;

  // getParent(跳过非宿主组件,直接定位父宿主组件)
  for (let tempA: null | Fiber = nodeA; tempA; tempA = getParent(tempA)) {
    depthA++;
  }
  
  let depthB = 0;
  for (let tempB: null | Fiber = nodeB; tempB; tempB = getParent(tempB)) {
    depthB++;
  }

  /** 对齐深度差 */

  // If A is deeper, crawl up.
  while (depthA - depthB > 0) {
    nodeA = getParent(nodeA);
    depthA--;
  }

  // If B is deeper, crawl up.
  while (depthB - depthA > 0) {
    nodeB = getParent(nodeB);
    depthB--;
  }

  // Walk in lockstep until we find a match.
  let depth = depthA;

  /** 同步向上查找公共祖先 */
  while (depth--) {
// 终止条件:
// 找到相同节点(nodeA === nodeB)
// 找到当前节点与备用节点(alternate)匹配(支持双缓存机制)
    if (nodeA === nodeB || (nodeB !== null && nodeA === nodeB.alternate)) {
      return nodeA;
    }
    nodeA = getParent(nodeA);
    nodeB = getParent(nodeB);
  }
  return null;
}

工具函数之 getParent

getParent 是 React 内部用于 查找指定 Fiber 节点的最近宿主组件父节点 的工具函数。 

function getParent(inst: Fiber | null): Fiber | null {
  if (inst === null) {
    return null;
  }
  // 向上遍历 Fiber 树(通过 return 指针)
  do {
    inst = inst.return;
// 终止条件:
// 找到 HostComponent(普通 DOM 元素)或 HostSingleton(特殊 DOM 元素,如 <input>)
// 或遍历到根节点(inst 变为 null)
  } while (inst && inst.tag !== HostComponent && inst.tag !== HostSingleton);
  
  if (inst) {
    return inst;
  }
  return null;
}

工具函数之 accumulateEnterLeaveListenersForEvent

accumulateEnterLeaveListenersForEvent 是 React 事件系统中处理 鼠标 / 指针进入离开事件 的核心函数。

函数参数含义:

  • dispatchQueue: 事件派发队列,存储待执行的事件监听器。
  • event: 合成事件对象(如 onMouseLeave)。
  • target: 事件目标 Fiber 节点(鼠标离开 / 进入的元素)。
  • common: 最近公共祖先节点,遍历终止条件。
  • inCapturePhase: 是否为捕获阶段(true 表示捕获,false 表示冒泡)。
function accumulateEnterLeaveListenersForEvent(
  dispatchQueue: DispatchQueue, // 事件派发队列
  event: KnownReactSyntheticEvent, // 合成事件对象
  target: Fiber, // 事件目标 Fiber 节点
  common: Fiber | null, //公共祖先节点
  inCapturePhase: boolean, // 是否为捕获阶段
): void {
  // 事件注册名(如 'onMouseLeave')
  const registrationName = event._reactName;
  // 存储收集到的监听器
  const listeners: Array<DispatchListener> = [];

  let instance: null | Fiber = target;
  
  while (instance !== null) {
    // 到达公共祖先节点,停止遍历
    if (instance === common) {
      break;
    }
    const {alternate, stateNode, tag} = instance;
    if (alternate !== null && alternate === common) {
      break;
    }
    
    // 处理宿主组件(DOM 元素)
    if (
      (tag === HostComponent ||
        tag === HostHoistable ||
        tag === HostSingleton) &&
      stateNode !== null
    ) {
      const currentTarget = stateNode;
      
      // 捕获阶段
      if (inCapturePhase) {
        const captureListener = getListener(instance, registrationName);
        if (captureListener != null) {
          // unshift:将监听器插入队列头部,确保从外向内执行(祖先 → 目标)。
          listeners.unshift(
            createDispatchListener(instance, captureListener, currentTarget),
          );
        }
        
      } else if (!inCapturePhase) {
        // 冒泡阶段=
        const bubbleListener = getListener(instance, registrationName);
        if (bubbleListener != null) {
          // push:将监听器插入队列尾部,确保从内向外执行(目标 → 祖先)。
          listeners.push(
            createDispatchListener(instance, bubbleListener, currentTarget),
          );
        }
      }
    }
    
    instance = instance.return;
  }
  
  if (listeners.length !== 0) {
    // 加入派发队列
    dispatchQueue.push({event, listeners});
  }
}

工具函数之 getListener

getListener 是 React 事件系统中用于 从 Fiber 节点获取事件监听器 的核心函数。

function getListener(
  inst: Fiber,
  registrationName: string,
): Function | null {
  // 获取 Fiber 对应的 DOM 节点(stateNode)
  const stateNode = inst.stateNode;
  if (stateNode === null) {
    // Work in progress (ex: onload events in incremental mode).
    return null;
  }
  
  // 从 DOM 节点获取当前组件的 props
  const props = getFiberCurrentPropsFromNode(stateNode);
  if (props === null) {
    // Work in progress.
    return null;
  }

  // 从 props 中提取事件监听器(如 props.onClick)
  const listener = props[registrationName];

  // 若元素禁用且为鼠标事件,返回 null(阻止执行)
  if (shouldPreventMouseEvent(registrationName, inst.type, props)) {
    return null;
  }

  if (listener && typeof listener !== 'function') {
    throw new Error(
      `Expected \`${registrationName}\` listener to be a function, instead got a value of \`${typeof listener}\` type.`,
    );
  }

  return listener;
}

工具函数之 getFiberCurrentPropsFromNode

getFiberCurrentPropsFromNode 是 React 内部用于 从 DOM 节点或 Suspense 实例获取对应 Fiber 节点的 props 的工具函数。

function getFiberCurrentPropsFromNode(
  node: Instance | TextInstance | SuspenseInstance,
): Props {
  return (node: any)[internalPropsKey] || null;
}

const internalPropsKey = '__reactProps$' + randomKey;

工具函数之 shouldPreventMouseEvent

shouldPreventMouseEvent 是 React 内部用于 判断是否阻止鼠标事件默认行为 的工具函数。

function shouldPreventMouseEvent(
  name: string,
  type: string,
  props: Props,
): boolean {
  switch (name) {
    case 'onClick':
    case 'onClickCapture':
    case 'onDoubleClick':
    case 'onDoubleClickCapture':
    case 'onMouseDown':
    case 'onMouseDownCapture':
    case 'onMouseMove':
    case 'onMouseMoveCapture':
    case 'onMouseUp':
    case 'onMouseUpCapture':
    case 'onMouseEnter':
      // isInteractive 判断元素是否可交互的
      return !!(props.disabled && isInteractive(type));
    default:
      return false;
  }
}

  工具函数之 isReplayingEvent

// 全局变量 currentReplayingEvent,用于标记当前正在重放的原生事件
let currentReplayingEvent = null;

// 判断某个原生事件是否正在被重放。
function isReplayingEvent(event: AnyNativeEvent): boolean {
  return event === currentReplayingEvent;
}

// 标记某个原生事件开始重放。
function setReplayingEvent(event: AnyNativeEvent): void {
  currentReplayingEvent = event;
}

// 清除重放标记。
function resetReplayingEvent(): void {
  currentReplayingEvent = null;
}

工具函数之 getNodeFromInstance

获取 fiber 节点的实际 DOM 节点。 

function getNodeFromInstance(inst: Fiber): Instance | TextInstance {
  const tag = inst.tag;
  if (
    tag === HostComponent ||
    tag === HostHoistable ||
    tag === HostSingleton ||
    tag === HostText
  ) {
    // In Fiber this, is just the state node right now. We assume it will be
    // a host component or host text.
    return inst.stateNode;
  }

  // Without this first invariant, passing a non-DOM-component triggers the next
  // invariant for a missing parent, which is super confusing.
  throw new Error('getNodeFromInstance: Invalid argument.');
}

3、ChangeEventPlugin.extractEvents

React 事件系统中处理表单元素事件的核心逻辑,主要负责根据不同的表单元素类型和事件类型,选择合适的处理函数。

主要负责:

  1. 识别事件类型:根据目标元素类型和事件名称决定如何处理
  2. 生成合成事件:将原生 DOM 事件转换为 React 合成事件
  3. 处理特殊表单行为:如输入框值同步、焦点管理等

函数参数含义:

  • dispatchQueue: 事件派发队列,存储待执行的事件监听器。
  • domEventName:原生 DOM 事件的名称。
  • targetInst:事件目标对应的 Fiber 实例。
  • nativeEvent:原生的 DOM 事件对象。
  • nativeEventTarget:原生事件的目标元素。
  • eventSystemFlags:事件系统的标志,用于表示事件的状态和特性。
  • targetContainer:事件发生的目标容器。
function extractEvents(
  dispatchQueue: DispatchQueue,
  domEventName: DOMEventName,
  targetInst: null | Fiber, // 事件触发的 Fiber 节点。
  nativeEvent: AnyNativeEvent,
  nativeEventTarget: null | EventTarget,
  eventSystemFlags: EventSystemFlags,
  targetContainer: null | EventTarget,
) {
  // 获取目标节点
  // getNodeFromInstance:将 Fiber 节点转换为实际 DOM 节点。
  const targetNode = targetInst ? getNodeFromInstance(targetInst) : window;

  // getTargetInstFunc 事件处理函数
  let getTargetInstFunc, handleEventFunc;

   // 表单变更事件处理
  // shouldUseChangeEvent:判断是否应使用 change 事件(如 select 元素、input[type=checkbox] 等)。
  if (shouldUseChangeEvent(targetNode)) {
    // 获取处理 change 事件的 Fiber 节点。
    getTargetInstFunc = getTargetInstForChangeEvent;

     // 文本输入元素处理
    // isTextInputElement:判断是否为文本输入元素(如 input[type=text]、textarea)。
  } else if (isTextInputElement(((targetNode: any): HTMLElement))) {
    // isInputEventSupported:检测浏览器是否支持 input 事件
    if (isInputEventSupported) {
      getTargetInstFunc = getTargetInstForInputOrChangeEvent;
    } else {
      // getTargetInstForInputEventPolyfill:针对不支持 input 事件的浏览器,提供回退方案(如使用 keydown、paste 等事件模拟)。
      getTargetInstFunc = getTargetInstForInputEventPolyfill;
      handleEventFunc = handleEventsForInputEventPolyfill;
    }

    // 点击事件处理
    // shouldUseClickEvent:判断是否应使用 click 事件(如 button、label 等元素)。
  } else if (shouldUseClickEvent(targetNode)) {
    getTargetInstFunc = getTargetInstForClickEvent;

    // 自定义元素处理
    // isCustomElement:判断是否为自定义元素(如 Web Component)。
  } else if (
    targetInst &&
    isCustomElement(targetInst.elementType, targetInst.memoizedProps)
  ) {
    getTargetInstFunc = getTargetInstForChangeEvent;
  }

  // 创建并累积事件
  if (getTargetInstFunc) {
    // 获取目标实例并创建事件
    const inst = getTargetInstFunc(domEventName, targetInst);
    if (inst) {
      // 创建合成事件并添加到派发队列。
      createAndAccumulateChangeEvent(
        dispatchQueue,
        inst,
        nativeEvent,
        nativeEventTarget,
      );
      return;
    }
  }

  // 特殊事件处理(如输入事件填充)
  if (handleEventFunc) {
    // 处理特殊事件(如 input 事件的回退方案)。
    handleEventFunc(domEventName, targetNode, targetInst);
  }

  // 焦点事件特殊处理
  if (domEventName === 'focusout' && targetInst) {
    const props = targetInst.memoizedProps;
    // 处理受控输入元素的失焦事件,确保值的同步。
    handleControlledInputBlur(((targetNode: any): HTMLInputElement), props);
  }
}

事件处理类型

1、 表单变更事件处理 getTargetInstForChangeEvent

2、文本输入元素处理 getTargetInstForInputOrChangeEvent 、getTargetInstForInputEventPolyfill

3、点击事件处理 getTargetInstForClickEvent

4、自定义元素处理 getTargetInstForChangeEvent

工具函数1之 getTargetInstForChangeEvent(表单变更事件处理

getTargetInstForChangeEvent 是 React 事件系统中用于 定位 change 事件目标 Fiber 节点 的工具函数。

function getTargetInstForChangeEvent(
  domEventName: DOMEventName,
  targetInst: null | Fiber,
) {
  if (domEventName === 'change') {
    return targetInst;
  }
}

工具函数2之 getTargetInstForInputOrChangeEvent(文本输入元素处理)

React 事件系统中专门处理 输入 / 变更事件 的工具函数,主要用于判断目标节点的值是否发生变化,并返回对应的 Fiber 实例。

函数参数含义:

  • domEventName: 原生 DOM 事件名称(如 inputchange)。
  • targetInst: 事件触发的目标 Fiber 节点。
function getTargetInstForInputOrChangeEvent(
  domEventName: DOMEventName,
  targetInst: null | Fiber,
) {
  // 处理输入/变更事件
  if (domEventName === 'input' || domEventName === 'change') 
  // getInstIfValueChanged 函数:检查目标节点的值是否与 React 内部维护的值不同。
  // 如果不同,说明值发生了变化,返回该节点的 Fiber 实例。
  // 如果相同,说明值未变化(可能是重复事件或虚假触发),返回 null。
    return getInstIfValueChanged(targetInst);
  }
}

工具函数之 getInstIfValueChanged

React 事件系统中用于检测表单元素值是否变化的核心工具,主要用于在处理输入事件时避免不必要的更新。

 函数参数含义:

  • targetInst: 事件触发的目标 Fiber 节点,包含组件的内部状态和属性。
function getInstIfValueChanged(targetInst: Object) {
  // 从 Fiber 实例获取对应的 DOM 节点
  const targetNode = getNodeFromInstance(targetInst);

  // 检测值是否变更
  if (updateValueIfChanged(((targetNode: any): HTMLInputElement))) {
    return targetInst;
  }
}

工具函数之 updateValueIfChanged

updateValueIfChanged 是 React 内部用于 检测并更新表单元素值 的核心函数,主要解决 受控组件与 DOM 状态同步 的问题。 

function updateValueIfChanged(node: ElementWithValueTracker): boolean {

  // 空节点
  if (!node) {
    return false;
  }

  // 获取值追踪器
  const tracker = getTracker(node);
  
   // 若不存在追踪器,认为已完成更新
  if (!tracker) {
    return true;
  }

  // 获取追踪器上一次值
  const lastValue = tracker.getValue();
  // 从 DOM 节点读取的当前值
  const nextValue = getValueFromNode(node);
  // 若值不同,则更新追踪器
  if (nextValue !== lastValue) {
    tracker.setValue(nextValue);
    return true;
  }
  return false;
}

工具函数之 getTracker

getTracker 是 React 内部用于 获取 DOM 元素值追踪器 的工具函数。 

function getTracker(node: ElementWithValueTracker) {
  // _valueTracker 是 React 内部添加到 DOM 元素的私有属性
  return node._valueTracker;
}
interface ElementWithValueTracker extends HTMLInputElement {
  _valueTracker?: ?ValueTracker;
}

type ValueTracker = {
  getValue(): string,
  setValue(value: string): void,
  stopTracking(): void,
};

工具函数之 getValueFromNode

React 事件系统中用于从 DOM 节点获取值的工具函数,主要用于处理不同类型的表单元素。

主要职责:

  1. 处理不同表单元素:根据元素类型(普通输入框、复选框 / 单选框等)获取对应的值。
  2. 标准化返回值:将各种表单元素的值统一转换为字符串类型。
  3. 处理空值情况:当节点不存在时返回空字符串,避免异常。

函数参数含义:

  • node: DOM 节点(通常是表单元素,如 <input><textarea> 等)。
function getValueFromNode(node: HTMLInputElement): string {
  let value = '';
  
  if (!node) {
    return value;
  }

  // isCheckable:判断元素是否为复选框或单选框(type="checkbox" 或 type="radio")。
  if (isCheckable(node)) {
    // 将布尔值(checked 属性)转换为字符串 "true" 或 "false"。
    value = node.checked ? 'true' : 'false';
  } else {
    // 普通输入元素处理,获取 value 属性
    value = node.value;
  }

  return value;
}
function isCheckable(elem: HTMLInputElement) {
  const type = elem.type;
  const nodeName = elem.nodeName;
  return (
    nodeName &&
    nodeName.toLowerCase() === 'input' &&
    (type === 'checkbox' || type === 'radio')
  );
}

工具函数2之 getTargetInstForInputEventPolyfill( 文本输入元素处理)

React 事件系统中针对不支持 input 事件的浏览器提供的回退方案(Polyfill)。

 函数参数含义:

  • domEventName: 原生 DOM 事件名称(如 selectionchangekeyup)。
  • targetInst: 事件触发的目标 Fiber 节点。
function getTargetInstForInputEventPolyfill(
  domEventName: DOMEventName,
  targetInst: null | Fiber,
) {
  // 监听代替事件
  if (
    domEventName === 'selectionchange' ||
    domEventName === 'keyup' ||
    domEventName === 'keydown'
  ) {
    // activeElementInst:当前聚焦元素的 Fiber 实例。
   // getInstIfValueChanged:检查 DOM 值与 React 内部维护的值是否不同
    return getInstIfValueChanged(activeElementInst);
  }
}

工具函数之 handleEventsForInputEventPolyfill

React 事件系统中针对 不支持 input 事件的浏览器 提供的 输入事件监听回退方案

主要作用是:

  1. 模拟 input 事件:在不支持 input 事件的浏览器中,通过监听 focusinfocusout 事件来捕获输入变化。
  2. 启动 / 停止监听:在元素获得焦点时启动监听,失去焦点时停止监听。
  3. 轮询检测值变化:通过定期检查 DOM 值的变化来模拟实时输入事件。

函数参数含义:

  • domEventName: 原生 DOM 事件名称(如 focusinfocusout)。
  • target: 事件目标 DOM 节点(输入元素)。
  • targetInst: 事件目标 Fiber 节点。
function handleEventsForInputEventPolyfill(
  domEventName: DOMEventName,
  target: Instance | TextInstance,
  targetInst: null | Fiber,
) {
  // 焦点获得处理
  if (domEventName === 'focusin') {
   // stopWatchingForValueChange():停止之前的所有轮询监听(确保唯一性)。
    stopWatchingForValueChange();
    // startWatchingForValueChange(target, targetInst):启动对当前元素的值变化监听。
    startWatchingForValueChange(target, targetInst);

    // 焦点失去事件处理
  } else if (domEventName === 'focusout') {
    // stopWatchingForValueChange():停止当前元素的值变化监听,释放资源。
    stopWatchingForValueChange();
  }
}

工具函数之 stopWatchingForValueChange

React 事件系统中用于停止监听输入值变化的函数,主要针对不支持 input 事件的旧版浏览器(如 IE9 及以下)。

function stopWatchingForValueChange() {
  // 如果没有活动元素,直接返回,避免不必要的操作。
  if (!activeElement) {
    return;
  }
  // detachEvent:IE 浏览器特有的方法,用于移除事件监听器。
  // propertychange 事件:IE 特有的事件,当元素的属性值发生变化时触发。
  // handlePropertyChange:事件处理函数,用于检测输入值的变化
  (activeElement: any).detachEvent('onpropertychange', handlePropertyChange);

  // 重置全局状态
  activeElement = null;
  activeElementInst = null;
}

工具函数之 startWatchingForValueChange

React 事件系统中用于监听输入值变化的函数,主要针对不支持 input 事件的旧版浏览器(如 IE9 及以下)。

function startWatchingForValueChange(
  target: Instance | TextInstance,
  targetInst: null | Fiber,
) {
  // activeElement:全局变量,存储当前正在监听的 DOM 元素。
  activeElement = target;
  
  // activeElementInst:全局变量,存储对应的 Fiber 节点。
  activeElementInst = targetInst;

  // attachEvent:IE 浏览器特有的方法,用于添加事件监听器。
  // propertychange 事件:IE 特有的事件,当元素的属性值发生变化时触发。
  // handlePropertyChange:事件处理函数,用于检测输入值的变化。
  (activeElement: any).attachEvent('onpropertychange', handlePropertyChange);
}

工具函数之 handlePropertyChange

React 事件系统中针对 IE 浏览器 propertychange 事件 的处理函数,主要用于在旧版浏览器中模拟 input 事件的行为。

handlePropertyChange 的主要作用是:

  1. 过滤属性变化:只处理 value 属性的变化,忽略其他属性(如 styleclassName)。
  2. 检测值变化:通过 getInstIfValueChanged 确认值确实发生了变化。
  3. 手动触发变更事件:如果值变化,调用 manualDispatchChangeEvent 模拟 change 事件。
function handlePropertyChange(nativeEvent) {
  // 只关注 value 属性的变化,其他属性变化直接忽略。
  if (nativeEvent.propertyName !== 'value') {
    return;
  }

  // getInstIfValueChanged:检查 DOM 值与 React 内部维护的值是否不同。
  if (getInstIfValueChanged(activeElementInst)) {
    // manualDispatchChangeEvent:手动构建并派发 change 事件。
    //   创建合成事件对象。
    //   收集事件传播路径上的所有监听器。
    //   按顺序执行监听器(捕获阶段 → 目标 → 冒泡阶段)。
    manualDispatchChangeEvent(nativeEvent);
  }
}

工具函数之 manualDispatchChangeEvent

React 事件系统中用于手动触发变更事件的核心函数,主要用于在不支持现代事件 API 的浏览器中模拟 change 事件。

function manualDispatchChangeEvent(nativeEvent: AnyNativeEvent) {
  // 创建派发队列
  const dispatchQueue: DispatchQueue = [];

  // 收集变更事件监听器
  createAndAccumulateChangeEvent(
    dispatchQueue,
    activeElementInst,
    nativeEvent,
    getEventTarget(nativeEvent),
  );

  // 批量执行事件处理
  batchedUpdates(runEventInBatch, dispatchQueue);
}

 React 事件系统中用于执行事件派发队列的核心函数,主要负责按顺序处理事件监听器。

function runEventInBatch(dispatchQueue: DispatchQueue) {
  // processDispatchQueue:处理事件派发队列的核心函数。
  //   参数 1:事件派发队列(DispatchQueue),包含事件对象和监听器数组。
  //   参数 2:起始索引(0 表示从队列头部开始处理)。
  // 这个函数是 React 事件派发流程的入口点
  processDispatchQueue(dispatchQueue, 0);
}

工具函数3之 getTargetInstForClickEvent(点击事件处理)

React 事件系统中专门处理 点击事件(click) 的工具函数,主要用于检测点击操作是否导致表单元素的值发生变化。

 函数参数含义:

  • domEventName: 原生 DOM 事件名称(如 click)。
  • targetInst: 事件触发的目标 Fiber 节点。
function getTargetInstForClickEvent(
  domEventName: DOMEventName,
  targetInst: null | Fiber,
) {
  // 只处理 click 事件
  if (domEventName === 'click') {
    // getInstIfValueChanged:检查目标元素的 DOM 值与 React 内部维护的值是否不同。
    return getInstIfValueChanged(targetInst);
  }
}

createAndAccumulateChangeEvent

React 事件系统中处理 表单变更事件(onChange) 的核心逻辑,主要负责创建合成事件并将其加入派发队列。

主要负责:

  1. 标记状态恢复需求:确保事件处理后恢复焦点或滚动位置
  2. 收集事件监听器:从 Fiber 节点向上查找所有注册的 onChange 回调
  3. 创建合成事件:将原生 DOM 事件转换为 React 合成事件
  4. 加入派发队列:将事件和监听器组合加入队列等待处理

 函数参数含义:

  • dispatchQueue: 事件派发队列,存储待执行的事件和监听器。
  • inst: 事件触发的目标 Fiber 节点。
  • nativeEvent: 原生 DOM 事件对象(如 MouseEventKeyboardEvent)。
  • target: 事件目标 DOM 节点。
function createAndAccumulateChangeEvent(
  dispatchQueue: DispatchQueue,
  inst: null | Fiber,
  nativeEvent: AnyNativeEvent,
  target: null | EventTarget,
) {
  // Flag this event loop as needing state restore.
  // enqueueStateRestore:将目标 DOM 节点加入状态恢复队列。
  enqueueStateRestore(((target: any): Node));

  // 收集事件监听器
  const listeners = accumulateTwoPhaseListeners(inst, 'onChange');
  
  if (listeners.length > 0) {
    // 创建合成事件
    const event: ReactSyntheticEvent = new SyntheticEvent(
      'onChange',  // React 事件名称
      'change', // 原生事件名称
      null,  // 无宿主组件(由 React 内部处理)
      nativeEvent,  // 原生 DOM 事件对象
      target,   // 事件目标 DOM 节点
    );
    // 加入派发队列
    dispatchQueue.push({event, listeners});
  }
}

工具函数之 enqueueStateRestore

enqueueStateRestore 是 React 内部用于 管理状态恢复队列 的核心函数,主要解决 异步渲染过程中状态恢复顺序 的问题。

// 当前正在处理的目标节点(恢复焦点 / 滚动位置等状态)
let restoreTarget = null;
// 等待处理的目标节点队列(FIFO 队列)
let restoreQueue = null;

function enqueueStateRestore(target: Node): void {
  if (restoreTarget) {
    if (restoreQueue) {
      restoreQueue.push(target);
    } else {
      restoreQueue = [target];
    }
  } else {
    restoreTarget = target;
  }
}

工具函数之 isCustomElement

isCustomElement 是 React 内部用于 判断元素是否为自定义元素(Web Component) 的工具函数。 

function isCustomElement(tagName: string, props: Object): boolean {
  // 根据 Web Components 标准,自定义元素标签名必须包含连字符(-)
  if (tagName.indexOf('-') === -1) {
    return false;
  }
  switch (tagName) {
      // 黑名单过滤
    case 'annotation-xml':
    case 'color-profile':
    case 'font-face':
    case 'font-face-src':
    case 'font-face-uri':
    case 'font-face-format':
    case 'font-face-name':
    case 'missing-glyph':
      return false;
    default:
      return true;
  }
}

工具函数之 accumulateTwoPhaseListeners

React 事件系统中实现 事件捕获和冒泡机制 的核心函数,主要负责收集事件传播路径上的所有监听器。

函数参数含义:

  • targetFiber: 事件触发的目标 Fiber 节点。
  • reactName: React 事件名称(如 onClick)。
function accumulateTwoPhaseListeners(
  targetFiber: Fiber | null,
  reactName: string,
): Array<DispatchListener> {
  
  // 生成捕获阶段事件名:例如,将 onClick 转换为 onClickCapture。
  const captureName = reactName + 'Capture';
  const listeners: Array<DispatchListener> = [];
  let instance = targetFiber;

  // Accumulate all instances and listeners via the target -> root path.
  // 从目标节点向上遍历至根节点(instance.return 指向父 Fiber 节点)。
  while (instance !== null) {
    const {stateNode, tag} = instance;
    // Handle listeners that are on HostComponents (i.e. <div>)
    if (
      (tag === HostComponent ||
        tag === HostHoistable ||
        tag === HostSingleton) &&
      stateNode !== null
    ) {
      const currentTarget = stateNode;
      const captureListener = getListener(instance, captureName);
      if (captureListener != null) {
        listeners.unshift(
          createDispatchListener(instance, captureListener, currentTarget),
        );
      }
      const bubbleListener = getListener(instance, reactName);
      if (bubbleListener != null) {
        listeners.push(
          createDispatchListener(instance, bubbleListener, currentTarget),
        );
      }
    }
    instance = instance.return;
  }
  return listeners;
}

4、SelectEventPlugin.extractEvents

extractEvents 是 React 内部用于 事件提取与分发 的核心函数,主要解决 将原生 DOM 事件转换为 React 合成事件 的问题。

主要作用:

  1. 事件类型分发:根据原生事件名称(如 focusinmousedown)执行不同的处理逻辑。
  2. 状态管理:维护全局状态(如鼠标按下状态、活动元素)。
  3. 事件构造:针对特定事件类型(如选区变化、键盘事件)构造合成事件。

 函数参数含义:

  • dispatchQueue: 事件派发队列,存储待执行的事件监听器。
  • domEventName:原生 DOM 事件的名称。
  • targetInst:事件目标对应的 Fiber 实例。
  • nativeEvent:原生的 DOM 事件对象。
  • nativeEventTarget:原生事件的目标元素。
  • eventSystemFlags:事件系统的标志,用于表示事件的状态和特性。
  • targetContainer:事件发生的目标容器。
function extractEvents(
  dispatchQueue: DispatchQueue,
  domEventName: DOMEventName,
  targetInst: null | Fiber,
  nativeEvent: AnyNativeEvent,
  nativeEventTarget: null | EventTarget,
  eventSystemFlags: EventSystemFlags,
  targetContainer: EventTarget,
) {
  // 获取目标节点
  // getNodeFromInstance:将 Fiber 节点转换为实际 DOM 元素。
  const targetNode = targetInst ? getNodeFromInstance(targetInst) : window;

  switch (domEventName) {
      
  /** 焦点事件处理 */
      
  //  focusin:记录当前聚焦的输入元素或可编辑元素。
    case 'focusin':
      if (
        isTextInputElement((targetNode: any)) ||
        targetNode.contentEditable === 'true'
      ) {
        activeElement = targetNode;
        activeElementInst = targetInst;
        lastSelection = null;
      }
      break;

      // focusout:清除聚焦状态,释放资源。
    case 'focusout':
      activeElement = null;
      activeElementInst = null;
      lastSelection = null;
      break;

      /** 鼠标事件处理 */
    // mousedown:标记鼠标按下状态。
    case 'mousedown':
      mouseDown = true;
      break;

   // 处理右键菜单、鼠标抬起、拖拽释放
    case 'contextmenu':
    case 'mouseup':
    case 'dragend':
      // 标记鼠标释放
      mouseDown = false;
      // 构造选区事件(处理文本选择)。
      constructSelectEvent(dispatchQueue, nativeEvent, nativeEventTarget);
      break;

    /** 选区变化事件处理 */
    case 'selectionchange':
      if (skipSelectionChangeEvent) {
        break;
      }
      
    /** 键盘事件处理 */
    case 'keydown':
    case 'keyup':
      // 构造选区事件,处理键盘操作导致的文本选择变化。
      constructSelectEvent(dispatchQueue, nativeEvent, nativeEventTarget);
  }
}
let activeElement = null; // 记录当前聚焦的 文本输入元素
let activeElementInst = null; // 记录当前聚焦元素对应的 Fiber 实例
let lastSelection = null; // 缓存 上一次记录的选区信息
let mouseDown = false; // 鼠标是否处于按下状态

工具函数之 skipSelectionChangeEvent

skipSelectionChangeEvent 是 React 内部用于 检测是否需要跳过 selectionchange 事件监听 的布尔标志。

const skipSelectionChangeEvent =
  canUseDOM && 'documentMode' in document && document.documentMode <= 11;
// canUseDOM:判断是否在浏览器环境中运行
// 'documentMode' in document:检测是否为 IE 浏览器(documentMode 是 IE 特有的属性)
// document.documentMode <= 11:判断是否为 IE 11 或更低版本
const canUseDOM: boolean = !!(
  typeof window !== 'undefined' &&
  typeof window.document !== 'undefined' &&
  typeof window.document.createElement !== 'undefined'
);

工具函数之 constructSelectEvent

constructSelectEvent 是 React 内部用于 处理文本选择事件 的核心函数,主要解决 在文本选区变化时触发 onSelect 事件 的问题。

函数参数含义:

  • dispatchQueue: 事件派发队列,存储待执行的事件监听器。
  • nativeEvent:原生的 DOM 事件对象。
  • nativeEventTarget:原生事件的目标元素。
function constructSelectEvent(
  dispatchQueue: DispatchQueue,
  nativeEvent: AnyNativeEvent,
  nativeEventTarget: null | EventTarget,
) {

  // 获取document对象
  const doc = getEventTargetDocument(nativeEventTarget);

  // 只在当前聚焦元素上处理选区变化
  if (
    mouseDown ||
    activeElement == null ||
    activeElement !== getActiveElement(doc)
  ) {
    return;
  }

  // 使用 getSelection 获取当前选区
  const currentSelection = getSelection(activeElement);

  // 与上次保存的选区(lastSelection)比较
  if (!lastSelection || !shallowEqual(lastSelection, currentSelection)) {
    lastSelection = currentSelection;

    // 收集 onSelect 事件的监听函数
    const listeners = accumulateTwoPhaseListeners(
      activeElementInst,
      'onSelect',
    );
    
    if (listeners.length > 0) {
      // 创建合成事件
      const event: ReactSyntheticEvent = new SyntheticEvent(
        'onSelect',
        'select',
        null,
        nativeEvent,
        nativeEventTarget,
      );
      // 加入派发队列
      dispatchQueue.push({event, listeners});
      // 设置事件目标为当前活动元素
      event.target = activeElement;
    }
  }
  
}

工具函数之 getEventTargetDocument

getEventTargetDocument 是 React 内部用于 获取事件目标所属文档对象 的工具函数。

function getEventTargetDocument(eventTarget: any) {
  // 浏览器环境中,window 对象的 window 属性指向自身
  return eventTarget.window === eventTarget
    ? eventTarget.document
    : eventTarget.nodeType === DOCUMENT_NODE
    // 若是 Document 对象,则直接返回自身
      ? eventTarget
    // 返回节点所属的文档对象(ownerDocument)
      : eventTarget.ownerDocument;
}

工具函数之 getActiveElement

React 中用于获取当前活动元素的工具函数,主要处理不同浏览器环境下的兼容性问题。

function getActiveElement(doc: ?Document): ?Element {
  // 获取文档对象
  doc = doc || (typeof document !== 'undefined' ? document : undefined);
  
  if (typeof doc === 'undefined') {
    return null;
  }
  
  try {
    // activeElement:标准属性,返回当前获得焦点的元素。
    return doc.activeElement || doc.body;
    
  } catch (e) {
    // 异常处理:某些浏览器(如旧版 IE)在某些情况下可能抛出错误,此时回退到 doc.body。
    return doc.body;
  }
}

工具函数之 getSelection

getSelection 函数用于 获取 DOM 元素中的文本选择范围,根据元素类型智能选择不同的获取方式:

  • 对于输入框类元素(如 <input><textarea>),使用 selectionStart/End 属性
  • 对于其他元素(如 contentEditable 元素),使用 window.getSelection() API
function getSelection(node: any) {
  
  if ('selectionStart' in node && hasSelectionCapabilities(node)) {
    return {
      // HTMLTextAreaElement,HTMLInputElement等
      // https://developer.mozilla.org/en-US/docs/Web/API/HTMLTextAreaElement/selectionStart
      start: node.selectionStart, // 选中文本的起始位置
      end: node.selectionEnd, // 选中文本的结束位置
    };

    // 通用dom元素
  } else {
    const win =
      (node.ownerDocument && node.ownerDocument.defaultView) || window;

    // https://developer.mozilla.org/zh-CN/docs/Web/API/Window/getSelection
    const selection = win.getSelection();
    return {
      anchorNode: selection.anchorNode, // 选区起始节点
      anchorOffset: selection.anchorOffset, // 起始节点内的偏移量
      focusNode: selection.focusNode, // 选区结束节点
      focusOffset: selection.focusOffset, // 结束节点内的偏移量
    };
  }
}

工具函数之 hasSelectionCapabilities

hasSelectionCapabilities 是一个用于 判断 DOM 元素是否支持文本选择和操作 的工具函数。它通过检查元素类型和属性,确定该元素是否允许用户选择文本、获取选中文本范围或执行文本编辑操作。

 function hasSelectionCapabilities(elem) {
  const nodeName = elem && elem.nodeName && elem.nodeName.toLowerCase();
  return (
    nodeName &&
    ((nodeName === 'input' &&
      (elem.type === 'text' ||
        elem.type === 'search' ||
        elem.type === 'tel' ||
        elem.type === 'url' ||
        elem.type === 'password')) ||
      nodeName === 'textarea' ||
      elem.contentEditable === 'true')
  );
}

工具函数之 shallowEqual

shallowEqual 是 React 内部用于 浅比较两个对象是否相等 的工具函数 

function shallowEqual(objA: mixed, objB: mixed): boolean {
  if (is(objA, objB)) {
    return true;
  }

  // 排除非对象类型(如函数、日期等)
  if (
    typeof objA !== 'object' ||
    objA === null ||
    typeof objB !== 'object' ||
    objB === null
  ) {
    return false;
  }

  // 属性比较
  const keysA = Object.keys(objA);
  const keysB = Object.keys(objB);

  if (keysA.length !== keysB.length) {
    return false;
  }


  for (let i = 0; i < keysA.length; i++) {
    const currentKey = keysA[i];
    if (
      !hasOwnProperty.call(objB, currentKey) ||
      !is(objA[currentKey], objB[currentKey])
    ) {
      return false;
    }
  }

  return true;
}

工具函数之 objectIs

用于 判断两个值是否为同一个值。与 === 相比,它修复了两个特殊情况:+0-0 的区分,以及 NaN 的比较。

function is(x: any, y: any) {
  return (
    (x === y && (x !== 0 || 1 / x === 1 / y)) || (x !== x && y !== y) 
  );
}

const objectIs: (x: any, y: any) => boolean =
  typeof Object.is === 'function' ? Object.is : is;

// Object.is() 静态方法确定两个值是否为相同值。

5、BeforeInputEventPlugin.extractEvents

extractEvents 是 React 事件系统中用于 从原生事件提取并分类处理合成事件 的核心函数,主要负责 将单一原生事件映射到多个合成事件类型

函数参数含义:

  • dispatchQueue: 事件派发队列,存储待执行的事件监听器。
  • domEventName:原生 DOM 事件的名称。
  • targetInst:事件目标对应的 Fiber 实例。
  • nativeEvent:原生的 DOM 事件对象。
  • nativeEventTarget:原生事件的目标元素。
  • eventSystemFlags:事件系统的标志,用于表示事件的状态和特性。
  • targetContainer:事件发生的目标容器。
function extractEvents(
  dispatchQueue: DispatchQueue,
  domEventName: DOMEventName,
  targetInst: null | Fiber,
  nativeEvent: AnyNativeEvent,
  nativeEventTarget: null | EventTarget,
  eventSystemFlags: EventSystemFlags,
  targetContainer: EventTarget,
): void {
  // 处理输入法组合文本事件
  extractCompositionEvent(
    dispatchQueue,
    domEventName,
    targetInst,
    nativeEvent,
    nativeEventTarget,
  );

  // 处理输入前状态事件
  extractBeforeInputEvent(
    dispatchQueue,
    domEventName,
    targetInst,
    nativeEvent,
    nativeEventTarget,
  );
}

extractCompositionEvent

extractCompositionEvent 是 React 内部用于 处理输入法组合文本事件 的核心函数,主要解决 非直接输入场景下文本内容的捕获与处理 问题。

函数参数含义:

  • dispatchQueue: 事件派发队列,存储待执行的事件监听器。
  • domEventName:原生 DOM 事件的名称。
  • targetInst:事件目标对应的 Fiber 实例。
  • nativeEvent:原生的 DOM 事件对象。
  • nativeEventTarget:原生事件的目标元素。
function extractCompositionEvent(
  dispatchQueue: DispatchQueue,
  domEventName: DOMEventName,
  targetInst: null | Fiber,
  nativeEvent: AnyNativeEvent,
  nativeEventTarget: null | EventTarget,
) {
  let eventType;
  let fallbackData;

  // 原生支持:如果浏览器支持 composition 系列事件(如 compositionstart),直接使用。
  if (canUseCompositionEvent) {
    // 获取合成事件名称
    eventType = getCompositionEventType(domEventName);

    // 回退机制:在不支持的浏览器中,通过 keydown、keyup 等事件模拟输入法开始 / 结束。
    // isComposing:全局状态,标记当前是否处于输入法组合状态。
  } else if (!isComposing) {
    //isFallbackCompositionStart判断是否 不支持 compositionstart 事件的浏览器中模拟输入法开始 
    if (isFallbackCompositionStart(domEventName, nativeEvent)) {
      eventType = 'onCompositionStart';
    }

    //isFallbackCompositionEnd判断是否 在不支持 compositionend 事件的浏览器中模拟输入法结束
  } else if (isFallbackCompositionEnd(domEventName, nativeEvent)) {
    eventType = 'onCompositionEnd';
  }

  if (!eventType) {
    return null;
  }

  // 启动回退机制 和 非韩语输入法处理
  if (useFallbackCompositionData && !isUsingKoreanIME(nativeEvent)) {

    // 当前不在组合状态(!isComposing)
    if (!isComposing && eventType === 'onCompositionStart') {
      // FallbackCompositionStateInitialize:初始化输入法组合状态,开始记录输入内容。
      isComposing = FallbackCompositionStateInitialize(nativeEventTarget);
      
    } else if (eventType === 'onCompositionEnd') {
      if (isComposing) {
        // FallbackCompositionStateGetData:获取输入法组合期间的文本内容(如拼音输入过程中的字符)。
        fallbackData = FallbackCompositionStateGetData();
      }
    }
  }

  // 收集合成事件监听器
  const listeners = accumulateTwoPhaseListeners(targetInst, eventType);
  
  if (listeners.length > 0) {
    // 创建合成事件
    const event: ReactSyntheticEvent = new SyntheticCompositionEvent(
      eventType,
      domEventName,
      null,
      nativeEvent,
      nativeEventTarget,
    );
    // 加入派发队列
    dispatchQueue.push({event, listeners});

    // 由于韩语输入法的特殊性,需要通过回退机制获取完整的输入内容。
    if (fallbackData) {
      event.data = fallbackData;
      
    } else {
      // 自定义事件数据:从原生事件中提取输入法内容(如拼音、日文假名等)。
      // 获取event.detail.data
      const customData = getDataFromCustomEvent(nativeEvent);
      if (customData !== null) {
        event.data = customData;
      }
    }
  }

  
}

工具函数之 getCompositionEventType

getCompositionEventType 是 React 内部用于 将原生输入法事件映射到对应合成事件名称 的工具函数,主要解决 浏览器原生输入法事件(如 compositionstart)与 React 合成事件(如 onCompositionStart 之间的命名映射问题。

function getCompositionEventType(domEventName: DOMEventName) {
  switch (domEventName) {
    case 'compositionstart':
      return 'onCompositionStart';
    case 'compositionend':
      return 'onCompositionEnd';
    case 'compositionupdate':
      return 'onCompositionUpdate';
  }
}

工具函数之 isFallbackCompositionStart

isFallbackCompositionStart 是 React 内部用于 在不支持 compositionstart 事件的浏览器中模拟输入法开始 的检测函数。

function isFallbackCompositionStart(
  domEventName: DOMEventName,
  nativeEvent: any,
): boolean {
  // 仅处理键盘按下事件
  return domEventName === 'keydown' && nativeEvent.keyCode === START_KEYCODE;
}

const START_KEYCODE = 229;

工具函数之 isFallbackCompositionEnd

isFallbackCompositionEnd 是 React 内部用于 在不支持 compositionend 事件的浏览器中模拟输入法结束 的检测函数。

function isFallbackCompositionEnd(
  domEventName: DOMEventName,
  nativeEvent: any,
): boolean {
  switch (domEventName) {
    case 'keyup':
       // 处理按键释放事件
      // Command keys insert or clear IME input.
      return END_KEYCODES.indexOf(nativeEvent.keyCode) !== -1;
    case 'keydown':
      // 处理按键按下事件
      // Expect IME keyCode on each keydown. If we get any other
      // code we must have exited earlier.
      return nativeEvent.keyCode !== START_KEYCODE;
    case 'keypress':
    case 'mousedown':
    case 'focusout':
      // 处理其他可能导致输入法结束的事件
      // Events are not possible without cancelling IME.
      return true;
    default:
      return false;
  }
}
function isUsingKoreanIME(nativeEvent: any) {
  return nativeEvent.locale === 'ko';
}

工具函数之 FallbackCompositionStateInitialize

initialize初始化函数,用于 记录元素的初始状态

// 目标 DOM 元素
let root = null;

// 初始化时的文本内容
let startText = null;

// 备选文本内容(用于回滚)
let fallbackText = null;

function initialize(nativeEventTarget) {
  // 设置目标元素
  root = nativeEventTarget;
  
  // 保存初始文本
  startText = getText();
  return true;
}

工具函数之 getText

getText 函数用于 根据元素类型获取其文本内容。其核心逻辑是:

  • 优先检查元素是否有 value 属性(如输入框、文本域)
  • 若无 value 属性,则获取 textContent(如普通 DOM 元素) 
let root = null;

function getText() {
  if ('value' in root) {
    return root.value;
  }
  return root.textContent;
}

工具函数之 FallbackCompositionStateGetData

getData 函数实现了一个 文本差异分析算法,用于提取两次状态间的新增文本。

function getData() {
  if (fallbackText) {
    return fallbackText;
  }

  let start;
  const startValue = startText;
  const startLength = startValue.length;
  
  let end;
  const endValue = getText();
  const endLength = endValue.length;

  // 从前向后找差异起始点:
  for (start = 0; start < startLength; start++) {
    if (startValue[start] !== endValue[start]) {
      break;
    }
  }

  // 从后向前找差异结束点:
  const minEnd = startLength - start;
  for (end = 1; end <= minEnd; end++) {
    if (startValue[startLength - end] !== endValue[endLength - end]) {
      break;
    }
  }

  // 计算切片参数:
  const sliceTail = end > 1 ? 1 - end : undefined;
  
  fallbackText = endValue.slice(start, sliceTail);
  
  return fallbackText;
}

工具函数之 getDataFromCustomEvent

getDataFromCustomEvent 是 React 内部用于 从自定义事件中提取数据 的工具函数,主要解决 DOM 自定义事件(CustomEvent)与 React 合成事件 之间的数据传递问题。

function getDataFromCustomEvent(nativeEvent: any) {
  // CustomEvent 接口的 detail 只读属性返回初始化事件时传递的任何数据。
  const detail = nativeEvent.detail;
  if (typeof detail === 'object' && 'data' in detail) {
    return detail.data;
  }
  return null;
}

extractBeforeInputEvent

React 事件系统中处理 beforeinput 事件的核心逻辑,主要负责捕获用户输入的字符并创建合成事件。

function extractBeforeInputEvent(
  dispatchQueue: DispatchQueue,
  domEventName: DOMEventName,
  targetInst: null | Fiber,
  nativeEvent: AnyNativeEvent,
  nativeEventTarget: null | EventTarget,
) {
  let chars;

  // 原生支持:如果浏览器支持 beforeinput 或 textinput 事件,直接从中提取字符。
  if (canUseTextInputEvent) {
    chars = getNativeBeforeInputChars(domEventName, nativeEvent);
    
  } else {
    // 回退机制:在不支持的浏览器中,通过分析 keydown、paste 等事件模拟输入字符。
    chars = getFallbackBeforeInputChars(domEventName, nativeEvent);
  }

  
  if (!chars) {
    return null;
  }

  // 收集监听器:通过 accumulateTwoPhaseListeners 收集所有注册的 onBeforeInput 监听器。
  const listeners = accumulateTwoPhaseListeners(targetInst, 'onBeforeInput');
  if (listeners.length > 0) {
    // 创建合成事件:将原生事件包装为 SyntheticInputEvent
    const event: ReactSyntheticEvent = new SyntheticInputEvent(
      'onBeforeInput',
      'beforeinput',
      null,
      nativeEvent,
      nativeEventTarget,
    );
    // 加入派发队列:将事件和监听器组合加入队列
    dispatchQueue.push({event, listeners});

    // 设置 data 属性为输入字符
    event.data = chars;
  }
}
const canUseTextInputEvent = canUseDOM && 'TextEvent' in window && !documentMode;

工具函数之 getNativeBeforeInputChars

getNativeBeforeInputChars 是 React 内部用于 从原生输入事件中提取即将插入的字符 的工具函数。

function getNativeBeforeInputChars(
  domEventName: DOMEventName,
  nativeEvent: any,
): ?string {
  switch (domEventName) {
  // 输入法组合文本结束
    case 'compositionend':

      return getDataFromCustomEvent(nativeEvent);

      // 键盘按下(已弃用,但仍需兼容)
    case 'keypress':
      // UIEvent 接口的 UIEvent.which 只读属性返回一个数字,表示按下了鼠标上的哪个按钮,或者是键盘上按下的键的 keyCode 或字符代码(charCode)的数字值。
      const which = nativeEvent.which;
      //  SPACEBAR_CODE = 32;
      if (which !== SPACEBAR_CODE) {
        return null;
      }

      hasSpaceKeypress = true;
      return SPACEBAR_CHAR;

      // 文本输入(较新的 API)
    case 'textInput':
      // 空格键可能同时触发 keypress 和 textInput 事件
      // Record the characters to be added to the DOM.
      const chars = nativeEvent.data;
      if (chars === SPACEBAR_CHAR && hasSpaceKeypress) {
        return null;
      }

      return chars;

       // 其他事件(如 click、scroll)返回 null
    default:
      // For other native event types, do nothing.
      return null;
  }
}

工具函数之 getFallbackBeforeInputChars

React 在不支持 beforeinput 事件的浏览器中,用于模拟获取输入字符的回退方案。

function getFallbackBeforeInputChars(
  domEventName: DOMEventName,
  nativeEvent: any,
): ?string {
 // isComposing:标记当前是否处于输入法组合状态(如拼音输入过程)。
  if (isComposing) {
    
    if (
      domEventName === 'compositionend' ||
      (!canUseCompositionEvent &&
       // 兼容性处理:在不支持 composition 事件的浏览器中,通过 isFallbackCompositionEnd 模拟检测组合结束。
        isFallbackCompositionEnd(domEventName, nativeEvent))
    ) {
      // 组合结束:当输入法组合结束时(compositionend 事件),获取并返回组合后的文本
      const chars = FallbackCompositionStateGetData();
      // 重置
      FallbackCompositionStateReset();
      isComposing = false;
      return chars;
    }
    return null;
  }

  switch (domEventName) {
    case 'paste':
     // 粘贴操作由专门的事件处理
      return null;
      
    case 'keypress':

      // 过滤快捷键命令(如 Ctrl+C、Alt+Tab)。
      if (!isKeypressCommand(nativeEvent)) {
       // 从 nativeEvent.char 或 nativeEvent.which 获取输入字符。
        if (nativeEvent.char && nativeEvent.char.length > 1) {
          return nativeEvent.char;
        } else if (nativeEvent.which) {
          return String.fromCharCode(nativeEvent.which);
        }
      }
      return null;
      
    case 'compositionend':
      // 处理韩语输入法等特殊场景。
      return useFallbackCompositionData && !isUsingKoreanIME(nativeEvent)
        ? null
        // 直接从 nativeEvent.data 获取组合后的文本。
        : nativeEvent.data;
    default:
      return null;
  }
}

function reset() {
  root = null;
  startText = null;
  fallbackText = null;
}
function isKeypressCommand(nativeEvent: any) {
  return (
    (nativeEvent.ctrlKey || nativeEvent.altKey || nativeEvent.metaKey) &&
    // ctrlKey && altKey is equivalent to AltGr, and is not a command.
    !(nativeEvent.ctrlKey && nativeEvent.altKey)
  );
}

6、FormActionEventPlugin.extractEvents

extractEvents 是 React 内部用于 处理表单提交事件 的核心函数,主要解决 原生表单提交行为与 React 合成事件系统的集成 问题。

函数参数含义:

  • dispatchQueue: 事件派发队列,存储待执行的事件监听器。
  • domEventName:原生 DOM 事件的名称。
  • targetInst:事件目标对应的 Fiber 实例。
  • nativeEvent:原生的 DOM 事件对象。
  • nativeEventTarget:原生事件的目标元素。
  • eventSystemFlags:事件系统的标志,用于表示事件的状态和特性。
  • targetContainer:事件发生的目标容器。
function extractEvents(
  dispatchQueue: DispatchQueue,
  domEventName: DOMEventName,
  maybeTargetInst: null | Fiber,
  nativeEvent: AnyNativeEvent,
  nativeEventTarget: null | EventTarget,
  eventSystemFlags: EventSystemFlags,
  targetContainer: EventTarget,
) {
  // 仅处理表单提交事件:忽略其他类型事件(如 click、input 等)
  if (domEventName !== 'submit') {
    return;
  }
  
  // maybeTargetInst:React 组件实例(Fiber)
  // nativeEventTarget:DOM 中的表单元素
  if (!maybeTargetInst || maybeTargetInst.stateNode !== nativeEventTarget) {
    return;
  }
  
  const formInst = maybeTargetInst;
  const form: HTMLFormElement = (nativeEventTarget: any);

  // 提取表单 action 属性
  let action = coerceFormActionProp(
    (getFiberCurrentPropsFromNode(form): any).action,
  );

  // 在 SubmitEvent 接口的只读属性 submitter 用于指定提交按钮或者被用来提交表单的其他元素。
  let submitter: null | void | HTMLInputElement | HTMLButtonElement =
    (nativeEvent: any).submitter;

  // 处理 submitter 的 formAction 属性
  let submitterAction;
  
  if (submitter) {
    
    const submitterProps = getFiberCurrentPropsFromNode(submitter);
    
    submitterAction = submitterProps
      ? coerceFormActionProp((submitterProps: any).formAction)
      : 
        ((submitter.getAttribute('formAction'): any): string | null);
    
    if (submitterAction !== null) {
      action = submitterAction;
      submitter = null;
    }
  }

  // 创建合成事件
  const event = new SyntheticEvent(
    'action', // 事件名称
    'action',
    null,
    nativeEvent, // 原生 DOM 事件
    nativeEventTarget, // 事件目标 DOM 元素
  );

  function submitForm(){
    // 省略代码
  }

  // 事件分发队列
  dispatchQueue.push({
    event,
    listeners: [
      {
        instance: null,
        listener: submitForm,
        currentTarget: form,
      },
    ],
  });
}
function submitForm() {
  // nativeEvent.defaultPrevented:检查是否已调用 event.preventDefault()
  if (nativeEvent.defaultPrevented) {
    
    // didCurrentEventScheduleTransition():检查当前事件是否安排了过渡动画。
    if (didCurrentEventScheduleTransition()) {

      // 创建表单数据
      const formData = submitter
        ? createFormDataWithSubmitter(form, submitter)
        : new FormData(form);
      
      const pendingState: FormStatus = {
        pending: true,
        data: formData,
        method: form.method,
        action: action,
      };

       // 启动过渡动画并提交表单
      startHostTransition(
        formInst,
        pendingState,
        null,
        formData,
      );
    } 

     // action 为函数:表示用户提供了自定义表单处理函数。
  } else if (typeof action === 'function') {
    
     // 阻止原生提交
    event.preventDefault();

    // 创建表单数据
    const formData = submitter
      ? createFormDataWithSubmitter(form, submitter)
      : new FormData(form);

    // 设置表单状态为 pending
    const pendingState: FormStatus = {
      pending: true,
      data: formData,
      method: form.method,
      action: action,
    };

    // 启动过渡并执行自定义 action
   
    startHostTransition(formInst, pendingState, action, formData);
    
  } else {
    
  }
}

工具函数之 coerceFormActionProp

coerceFormActionProp 是 React 内部用于 规范化表单 action 属性值 的工具函数,主要解决 不同类型输入值到合法表单提交目标的转换 问题。

function coerceFormActionProp(
  actionProp: mixed,
): string | (FormData => void | Promise<void>) | null {
  // This should match the logic in ReactDOMComponent
  if (
    actionProp == null ||
    typeof actionProp === 'symbol' ||
    typeof actionProp === 'boolean'
  ) {
    // null/undefined/symbol/boolean → null
    return null;
    
  } else if (typeof actionProp === 'function') {
    return (actionProp: any);
    
  } else {
    //  string → 经过 sanitizeURL 处理的字符串
    return (sanitizeURL(
      enableTrustedTypesIntegration ? actionProp : '' + (actionProp: any),
    ): any);
  }
}

const enableTrustedTypesIntegration = false;

工具函数之 didCurrentEventScheduleTransition

React 事件系统中用于检测当前事件是否安排了过渡动画的核心逻辑,主要通过 currentEventTransitionLane 全局变量来跟踪过渡状态。

let currentEventTransitionLane: Lane = NoLane;

function didCurrentEventScheduleTransition(): boolean {
  // 如果 currentEventTransitionLane 不为 NoLane,说明当前事件中安排了过渡任务。
  return currentEventTransitionLane !== NoLane;
}

工具函数之 createFormDataWithSubmitter

createFormDataWithSubmitter 是 React 内部用于 创建包含提交按钮数据的 FormData 对象 的工具函数。

function createFormDataWithSubmitter(
  form: HTMLFormElement,
  submitter: HTMLInputElement | HTMLButtonElement,
) {
  const temp = submitter.ownerDocument.createElement('input');
  temp.name = submitter.name;
  temp.value = submitter.value;

  // 当表单有 id 且提交按钮通过 form 属性关联到该表单时
  if (form.id) {
    temp.setAttribute('form', form.id);
  }
  
  (submitter.parentNode: any).insertBefore(temp, submitter);

  // 创建表单数据
  const formData = new FormData(form);

  // 移除临时创建的input
  (temp.parentNode: any).removeChild(temp);
  
  return formData;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2407685.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放

简介 前面两期文章我们介绍了I2S的读取和写入&#xff0c;一个是通过INMP441麦克风模块采集音频&#xff0c;一个是通过PCM5102A模块播放音频&#xff0c;那如果我们将两者结合起来&#xff0c;将麦克风采集到的音频通过PCM5102A播放&#xff0c;是不是就可以做一个扩音器了呢…

Nuxt.js 中的路由配置详解

Nuxt.js 通过其内置的路由系统简化了应用的路由配置&#xff0c;使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…

高等数学(下)题型笔记(八)空间解析几何与向量代数

目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…

屋顶变身“发电站” ,中天合创屋面分布式光伏发电项目顺利并网!

5月28日&#xff0c;中天合创屋面分布式光伏发电项目顺利并网发电&#xff0c;该项目位于内蒙古自治区鄂尔多斯市乌审旗&#xff0c;项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站&#xff0c;总装机容量为9.96MWp。 项目投运后&#xff0c;每年可节约标煤3670…

P3 QT项目----记事本(3.8)

3.8 记事本项目总结 项目源码 1.main.cpp #include "widget.h" #include <QApplication> int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); } 2.widget.cpp #include "widget.h" #include &q…

Cinnamon修改面板小工具图标

Cinnamon开始菜单-CSDN博客 设置模块都是做好的&#xff0c;比GNOME简单得多&#xff01; 在 applet.js 里增加 const Settings imports.ui.settings;this.settings new Settings.AppletSettings(this, HTYMenusonichy, instance_id); this.settings.bind(menu-icon, menu…

华为OD机试-食堂供餐-二分法

import java.util.Arrays; import java.util.Scanner;public class DemoTest3 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint a in.nextIn…

跨链模式:多链互操作架构与性能扩展方案

跨链模式&#xff1a;多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈&#xff1a;模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展&#xff08;H2Cross架构&#xff09;&#xff1a; 适配层&#xf…

MODBUS TCP转CANopen 技术赋能高效协同作业

在现代工业自动化领域&#xff0c;MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步&#xff0c;这两种通讯协议也正在被逐步融合&#xff0c;形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…

Keil 中设置 STM32 Flash 和 RAM 地址详解

文章目录 Keil 中设置 STM32 Flash 和 RAM 地址详解一、Flash 和 RAM 配置界面(Target 选项卡)1. IROM1(用于配置 Flash)2. IRAM1(用于配置 RAM)二、链接器设置界面(Linker 选项卡)1. 勾选“Use Memory Layout from Target Dialog”2. 查看链接器参数(如果没有勾选上面…

如何将联系人从 iPhone 转移到 Android

从 iPhone 换到 Android 手机时&#xff0c;你可能需要保留重要的数据&#xff0c;例如通讯录。好在&#xff0c;将通讯录从 iPhone 转移到 Android 手机非常简单&#xff0c;你可以从本文中学习 6 种可靠的方法&#xff0c;确保随时保持连接&#xff0c;不错过任何信息。 第 1…

苍穹外卖--缓存菜品

1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得&#xff0c;如果用户端访问量比较大&#xff0c;数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据&#xff0c;减少数据库查询操作。 缓存逻辑分析&#xff1a; ①每个分类下的菜品保持一份缓存数据…

Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持续更新中&#xff01;&#xff08;长期更新&#xff09; 目前2025年06月05日更新到&#xff1a; AI炼丹日志-28 - Aud…

【2025年】解决Burpsuite抓不到https包的问题

环境&#xff1a;windows11 burpsuite:2025.5 在抓取https网站时&#xff0c;burpsuite抓取不到https数据包&#xff0c;只显示&#xff1a; 解决该问题只需如下三个步骤&#xff1a; 1、浏览器中访问 http://burp 2、下载 CA certificate 证书 3、在设置--隐私与安全--…

DIY|Mac 搭建 ESP-IDF 开发环境及编译小智 AI

前一阵子在百度 AI 开发者大会上&#xff0c;看到基于小智 AI DIY 玩具的演示&#xff0c;感觉有点意思&#xff0c;想着自己也来试试。 如果只是想烧录现成的固件&#xff0c;乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外&#xff0c;还提供了基于网页版的 ESP LA…

学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1

每日一言 生活的美好&#xff0c;总是藏在那些你咬牙坚持的日子里。 硬件&#xff1a;OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写&#xff0c;"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …

视频字幕质量评估的大规模细粒度基准

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 摘要 视频字幕在文本到视频生成任务中起着至关重要的作用&#xff0c;因为它们的质量直接影响所生成视频的语义连贯性和视觉保真度。尽管大型视觉-语言模型&#xff08;VLMs&#xff09;在字幕生成方面…

SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现

摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序&#xff0c;以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务&#xff0c;提供稳定高效的数据处理与业务逻辑支持&#xff1b;利用 uniapp 实现跨平台前…

2025 后端自学UNIAPP【项目实战:旅游项目】6、我的收藏页面

代码框架视图 1、先添加一个获取收藏景点的列表请求 【在文件my_api.js文件中添加】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口&#xff08;适配服务端返回 Token&#xff09; export const login async (code, avatar) > {const res await http…