React19源码系列之 事件插件系统
事件类别
事件类型 | 定义 | 文档 |
|
| Event - Web API | MDN |
|
| UIEvent - Web API | MDN |
|
| KeyboardEvent - Web API | MDN |
|
| 鼠标事件 - Web API | MDN |
PointerEvent | PointerEvent 接口代表了由 指针 引发的 DOM 事件的状态,包括接触点的位置,引发事件的设备类型,接触表面受到的压力等。 | PointerEvent - Web API | MDN |
|
| FocusEvent - Web API | MDN |
|
| TouchEvent - Web API | MDN |
|
| DragEvent - Web API | MDN |
|
| WheelEvent - Web API | MDN |
|
| ToggleEvent - Web API | MDN |
TransitonEvent | TransitonEvent 接口指那些提供了与过渡有关信息的事件。 | TransitionEvent - Web API | MDN |
|
| InputEvent - Web API | MDN |
| DOM 接口 | CompositionEvent - Web API | MDN |
| The | AnimationEvent - Web APIs | MDN |
|
| 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 事件(如 |
EnterLeaveEventPlugin | 处理鼠标进入 / 离开事件( |
ChangeEventPlugin | 处理表单元素的变更事件(如 |
SelectEventPlugin | 处理文本选择事件(如 |
BeforeInputEventPlugin | 处理输入前事件( |
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 内部事件名称(如click
、change
)。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 事件系统中处理表单元素事件的核心逻辑,主要负责根据不同的表单元素类型和事件类型,选择合适的处理函数。
主要负责:
- 识别事件类型:根据目标元素类型和事件名称决定如何处理
- 生成合成事件:将原生 DOM 事件转换为 React 合成事件
- 处理特殊表单行为:如输入框值同步、焦点管理等
函数参数含义:
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 事件名称(如input
、change
)。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 节点获取值的工具函数,主要用于处理不同类型的表单元素。
主要职责:
- 处理不同表单元素:根据元素类型(普通输入框、复选框 / 单选框等)获取对应的值。
- 标准化返回值:将各种表单元素的值统一转换为字符串类型。
- 处理空值情况:当节点不存在时返回空字符串,避免异常。
函数参数含义:
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 事件名称(如selectionchange
、keyup
)。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
事件的浏览器 提供的 输入事件监听回退方案。
主要作用是:
- 模拟
input
事件:在不支持input
事件的浏览器中,通过监听focusin
和focusout
事件来捕获输入变化。 - 启动 / 停止监听:在元素获得焦点时启动监听,失去焦点时停止监听。
- 轮询检测值变化:通过定期检查 DOM 值的变化来模拟实时输入事件。
函数参数含义:
domEventName
: 原生 DOM 事件名称(如focusin
、focusout
)。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
的主要作用是:
- 过滤属性变化:只处理
value
属性的变化,忽略其他属性(如style
、className
)。 - 检测值变化:通过
getInstIfValueChanged
确认值确实发生了变化。 - 手动触发变更事件:如果值变化,调用
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) 的核心逻辑,主要负责创建合成事件并将其加入派发队列。
主要负责:
- 标记状态恢复需求:确保事件处理后恢复焦点或滚动位置
- 收集事件监听器:从 Fiber 节点向上查找所有注册的
onChange
回调 - 创建合成事件:将原生 DOM 事件转换为 React 合成事件
- 加入派发队列:将事件和监听器组合加入队列等待处理
函数参数含义:
dispatchQueue
: 事件派发队列,存储待执行的事件和监听器。inst
: 事件触发的目标 Fiber 节点。nativeEvent
: 原生 DOM 事件对象(如MouseEvent
、KeyboardEvent
)。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 合成事件 的问题。
主要作用:
- 事件类型分发:根据原生事件名称(如
focusin
、mousedown
)执行不同的处理逻辑。 - 状态管理:维护全局状态(如鼠标按下状态、活动元素)。
- 事件构造:针对特定事件类型(如选区变化、键盘事件)构造合成事件。
函数参数含义:
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的读取和写入,一个是通过INMP441麦克风模块采集音频,一个是通过PCM5102A模块播放音频,那如果我们将两者结合起来,将麦克风采集到的音频通过PCM5102A播放,是不是就可以做一个扩音器了呢…...

Nuxt.js 中的路由配置详解
Nuxt.js 通过其内置的路由系统简化了应用的路由配置,使得开发者可以轻松地管理页面导航和 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日,中天合创屋面分布式光伏发电项目顺利并网发电,该项目位于内蒙古自治区鄂尔多斯市乌审旗,项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站,总装机容量为9.96MWp。 项目投运后,每年可节约标煤3670…...
使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装
以下是基于 vant-ui(适配 Vue2 版本 )实现截图中照片上传预览、删除功能,并封装成可复用组件的完整代码,包含样式和逻辑实现,可直接在 Vue2 项目中使用: 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 的滤镜命令是用于音视频处理中的强大工具,可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下: ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜: ffmpeg…...
第25节 Node.js 断言测试
Node.js的assert模块主要用于编写程序的单元测试时使用,通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试,通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...

Cinnamon修改面板小工具图标
Cinnamon开始菜单-CSDN博客 设置模块都是做好的,比GNOME简单得多! 在 applet.js 里增加 const Settings imports.ui.settings;this.settings new Settings.AppletSettings(this, HTYMenusonichy, instance_id); this.settings.bind(menu-icon, menu…...
【论文笔记】若干矿井粉尘检测算法概述
总的来说,传统机器学习、传统机器学习与深度学习的结合、LSTM等算法所需要的数据集来源于矿井传感器测量的粉尘浓度,通过建立回归模型来预测未来矿井的粉尘浓度。传统机器学习算法性能易受数据中极端值的影响。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 证书
🔑 1. 生成 SSH 密钥对 在终端(Windows 使用 Git Bash,Mac/Linux 使用 Terminal)执行命令: ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" 参数说明: -t rsa&#x…...
【算法训练营Day07】字符串part1
文章目录 反转字符串反转字符串II替换数字 反转字符串 题目链接:344. 反转字符串 双指针法,两个指针的元素直接调转即可 class Solution {public void reverseString(char[] s) {int head 0;int end s.length - 1;while(head < end) {char temp …...

跨链模式:多链互操作架构与性能扩展方案
跨链模式:多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈:模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展(H2Cross架构): 适配层…...
镜像里切换为普通用户
如果你登录远程虚拟机默认就是 root 用户,但你不希望用 root 权限运行 ns-3(这是对的,ns3 工具会拒绝 root),你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案:创建非 roo…...

MODBUS TCP转CANopen 技术赋能高效协同作业
在现代工业自动化领域,MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步,这两种通讯协议也正在被逐步融合,形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...
spring:实例工厂方法获取bean
spring处理使用静态工厂方法获取bean实例,也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下: 定义实例工厂类(Java代码),定义实例工厂(xml),定义调用实例工厂ÿ…...

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 手机时,你可能需要保留重要的数据,例如通讯录。好在,将通讯录从 iPhone 转移到 Android 手机非常简单,你可以从本文中学习 6 种可靠的方法,确保随时保持连接,不错过任何信息。 第 1…...

苍穹外卖--缓存菜品
1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得,如果用户端访问量比较大,数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据,减少数据库查询操作。 缓存逻辑分析: ①每个分类下的菜品保持一份缓存数据…...
Qt Http Server模块功能及架构
Qt Http Server 是 Qt 6.0 中引入的一个新模块,它提供了一个轻量级的 HTTP 服务器实现,主要用于构建基于 HTTP 的应用程序和服务。 功能介绍: 主要功能 HTTP服务器功能: 支持 HTTP/1.1 协议 简单的请求/响应处理模型 支持 GET…...
Python爬虫(二):爬虫完整流程
爬虫完整流程详解(7大核心步骤实战技巧) 一、爬虫完整工作流程 以下是爬虫开发的完整流程,我将结合具体技术点和实战经验展开说明: 1. 目标分析与前期准备 网站技术分析: 使用浏览器开发者工具(F12&…...
Nginx server_name 配置说明
Nginx 是一个高性能的反向代理和负载均衡服务器,其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机(Virtual Host)。 1. 简介 Nginx 使用 server_name 指令来确定…...

Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
【Go】3、Go语言进阶与依赖管理
前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课,做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程,它的核心机制是 Goroutine 协程、Channel 通道,并基于CSP(Communicating Sequential Processes࿰…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院查看报告小程序
一、开发环境准备 工具安装: 下载安装DevEco Studio 4.0(支持HarmonyOS 5)配置HarmonyOS SDK 5.0确保Node.js版本≥14 项目初始化: ohpm init harmony/hospital-report-app 二、核心功能模块实现 1. 报告列表…...

【2025年】解决Burpsuite抓不到https包的问题
环境:windows11 burpsuite:2025.5 在抓取https网站时,burpsuite抓取不到https数据包,只显示: 解决该问题只需如下三个步骤: 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 开发者大会上,看到基于小智 AI DIY 玩具的演示,感觉有点意思,想着自己也来试试。 如果只是想烧录现成的固件,乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外,还提供了基于网页版的 ESP LA…...