当前位置: 首页 > article >正文

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

事件类别

事件类型

定义

文档

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 和 charCodewhich: 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, // 屏幕坐标 XscreenY: 0, // 屏幕坐标 YclientX: 0,  // 客户端坐标 XclientY: 0, // 客户端坐标 YpageX: 0,   // 页面坐标 XpageY: 0,   // 页面坐标 YctrlKey: 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/toElementif (event.relatedTarget === undefined)return event.fromElement === event.srcElement? event.toElement // mouseleave 场景: event.fromElement; // mouseenter 场景// 现代浏览器直接返回 relatedTargetreturn 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,// 目标 FiberreactName,// 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, //鼠标离开的元素对应的 Fiberto: 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 节点获取当前组件的 propsconst 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/selectionStartstart: 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/getSelectionconst 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.dataconst 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) {// 创建合成事件:将原生事件包装为 SyntheticInputEventconst 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)返回 nulldefault:// 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);// 设置表单状态为 pendingconst pendingState: FormStatus = {pending: true,data: formData,method: form.method,action: action,};// 启动过渡并执行自定义 actionstartHostTransition(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 ReactDOMComponentif (actionProp == null ||typeof actionProp === 'symbol' ||typeof actionProp === 'boolean') {// null/undefined/symbol/boolean → nullreturn 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;
}

相关文章:

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

事件类别 事件类型 定义 文档 Event Event 接口表示在 EventTarget 上出现的事件。 Event - Web API | MDN UIEvent UIEvent 接口表示简单的用户界面事件。 UIEvent - Web API | MDN KeyboardEvent KeyboardEvent 对象描述了用户与键盘的交互。 KeyboardEvent - Web…...

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…...

使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装

以下是基于 vant-ui&#xff08;适配 Vue2 版本 &#xff09;实现截图中照片上传预览、删除功能&#xff0c;并封装成可复用组件的完整代码&#xff0c;包含样式和逻辑实现&#xff0c;可直接在 Vue2 项目中使用&#xff1a; 1. 封装的图片上传组件 ImageUploader.vue <te…...

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…...

ffmpeg(四):滤镜命令

FFmpeg 的滤镜命令是用于音视频处理中的强大工具&#xff0c;可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下&#xff1a; ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜&#xff1a; ffmpeg…...

第25节 Node.js 断言测试

Node.js的assert模块主要用于编写程序的单元测试时使用&#xff0c;通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试&#xff0c;通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...

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…...

【论文笔记】若干矿井粉尘检测算法概述

总的来说&#xff0c;传统机器学习、传统机器学习与深度学习的结合、LSTM等算法所需要的数据集来源于矿井传感器测量的粉尘浓度&#xff0c;通过建立回归模型来预测未来矿井的粉尘浓度。传统机器学习算法性能易受数据中极端值的影响。YOLO等计算机视觉算法所需要的数据集来源于…...

华为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…...

生成 Git SSH 证书

&#x1f511; 1. ​​生成 SSH 密钥对​​ 在终端&#xff08;Windows 使用 Git Bash&#xff0c;Mac/Linux 使用 Terminal&#xff09;执行命令&#xff1a; ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" ​​参数说明​​&#xff1a; -t rsa&#x…...

【算法训练营Day07】字符串part1

文章目录 反转字符串反转字符串II替换数字 反转字符串 题目链接&#xff1a;344. 反转字符串 双指针法&#xff0c;两个指针的元素直接调转即可 class Solution {public void reverseString(char[] s) {int head 0;int end s.length - 1;while(head < end) {char temp …...

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

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

镜像里切换为普通用户

如果你登录远程虚拟机默认就是 root 用户&#xff0c;但你不希望用 root 权限运行 ns-3&#xff08;这是对的&#xff0c;ns3 工具会拒绝 root&#xff09;&#xff0c;你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案&#xff1a;创建非 roo…...

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

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

spring:实例工厂方法获取bean

spring处理使用静态工厂方法获取bean实例&#xff0c;也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下&#xff1a; 定义实例工厂类&#xff08;Java代码&#xff09;&#xff0c;定义实例工厂&#xff08;xml&#xff09;&#xff0c;定义调用实例工厂&#xff…...

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; ①每个分类下的菜品保持一份缓存数据…...

Qt Http Server模块功能及架构

Qt Http Server 是 Qt 6.0 中引入的一个新模块&#xff0c;它提供了一个轻量级的 HTTP 服务器实现&#xff0c;主要用于构建基于 HTTP 的应用程序和服务。 功能介绍&#xff1a; 主要功能 HTTP服务器功能&#xff1a; 支持 HTTP/1.1 协议 简单的请求/响应处理模型 支持 GET…...

Python爬虫(二):爬虫完整流程

爬虫完整流程详解&#xff08;7大核心步骤实战技巧&#xff09; 一、爬虫完整工作流程 以下是爬虫开发的完整流程&#xff0c;我将结合具体技术点和实战经验展开说明&#xff1a; 1. 目标分析与前期准备 网站技术分析&#xff1a; 使用浏览器开发者工具&#xff08;F12&…...

Nginx server_name 配置说明

Nginx 是一个高性能的反向代理和负载均衡服务器&#xff0c;其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机&#xff08;Virtual Host&#xff09;。 1. 简介 Nginx 使用 server_name 指令来确定…...

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…...

【Go】3、Go语言进阶与依赖管理

前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课&#xff0c;做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程&#xff0c;它的核心机制是 Goroutine 协程、Channel 通道&#xff0c;并基于CSP&#xff08;Communicating Sequential Processes&#xff0…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院查看报告小程序

一、开发环境准备 ​​工具安装​​&#xff1a; 下载安装DevEco Studio 4.0&#xff08;支持HarmonyOS 5&#xff09;配置HarmonyOS SDK 5.0确保Node.js版本≥14 ​​项目初始化​​&#xff1a; ohpm init harmony/hospital-report-app 二、核心功能模块实现 1. 报告列表…...

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

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

[10-3]软件I2C读写MPU6050 江协科技学习笔记(16个知识点)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16...

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

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