React 合成事件Synthetic events是 React 框架中用于优化事件处理的核心机制,是一个完全符合 DOM3的事件系统:
- 跨浏览器一致性:通过统一封装,抹平了不同浏览器的事件系统差异(比如
IE11只支持冒泡、阻止冒泡是使用event.cancelBubble = true),有统一的 API 方便使用 - 性能优化:大部分使用事件委托减少了监听器数量,同时了避免了频繁监听、销毁事件的开销(滚动事件依然是单独元素监听)
合成事件的机制
在createRoot创建根节点时,通过事件委托来监听所有事件,后续在通过分发来触发我们代码中的回调,整个事件的流程,React 使用插件机制来实现:
1. 事件的注册
React 在初始化时,所有的事件注册在引入DOMPluginEventSystem.js全局执行:
//src/react/packages/react-dom/src/events/DOMPluginEventSystem.js
SimpleEventPlugin.registerEvents();//最终触发registerDirectEvent
EnterLeaveEventPlugin.registerEvents();
//src/react/packages/react-dom/src/events/EventRegistry.js
// 全局变量、全局监听也是使用allNativeEvents
export const allNativeEvents: Set<DOMEventName> = new Set();
//registerEvents最终触发registerDirectEvent
export function registerDirectEvent(
registrationName: string,
dependencies: Array<DOMEventName>,
) {
for (let i = 0; i < dependencies.length; i++) {
allNativeEvents.add(dependencies[i]);
}
}
React 在注册时会将事件分为
- 离散事件:立即执行,抢占式调度(如click)
- 用户阻塞事件:微任务队列调度,避免阻塞渲染(如drag)
- 连续事件:宏任务队列调度,保证流畅性(如scroll)
通过createEventListenerWrapperWithPriority分配不同调度策略:
function createEventListenerWrapperWithPriority(...) {
const eventPriority = getEventPriorityForPluginSystem(domEventName); // 获取优先级
switch (eventPriority) {
case DiscreteEvent:
listenerWrapper = dispatchDiscreteEvent; // 离散事件策略
break;
case UserBlockingEvent:
listenerWrapper = dispatchUserBlockingUpdate; // 用户阻塞策略
break;
case ContinuousEvent:
listenerWrapper = dispatchEvent; // 连续事件策略
break;
}
return listenerWrapper.bind(...);
}
2. 顶层事件监听和分发
React在应用根容器(React 17+)或document(React 16及之前)上统一绑定事件监听:
- 遍历
allNativeEvents,为每个事件类型绑定捕获/冒泡阶段的监听器;
//src/react/packages/react-dom/src/client/ReactDOMRoot.js
export function listenToAllSupportedEvents(rootContainerElement: EventTarget) {
allNativeEvents.forEach(domEventName => {
listenToNativeEvent(
domEventName,
true,
((rootContainerElement: any): Element),
null,
);
});
}
- 事件代理函数
dispatchEvent作为统一回调,通过事件冒泡/捕获路径触发插件处理逻辑。
//src/react/packages/react-dom/src/events/ReactDOMEventListener.js
export function dispatchEvent(
domEventName: DOMEventName,
eventSystemFlags: EventSystemFlags,
targetContainer: EventTarget,
nativeEvent: AnyNativeEvent,
): void {
//...省略多余代码
//最终都会触发dispatchEvent -> dispatchEventForPluginEventSystem
dispatchEventForPluginEventSystem(
domEventName,
eventSystemFlags,
nativeEvent,
null,
targetContainer,
);
}
3. 事件的提取
当原生事件触发时,React通过插件分层处理机制生成合成事件:
- 事件分发入口:
dispatchEventForPluginEventSystem核心是调用dispatchEventsForPlugins,进而触发插件的extractEvents()方法收集所有事件回调,processDispatchQueue执行所有回调
//src/react/packages/react-dom/src/events/DOMPluginEventSystem.js
function dispatchEventsForPlugins(
domEventName: DOMEventName,
): void {
const dispatchQueue: DispatchQueue = []; //item 格式 {event, listeners}
//解析事件,获取 listeners 和 创建每个listener的 event
extractEvents(
dispatchQueue,
domEventName,
);
//触发事件
processDispatchQueue(dispatchQueue, eventSystemFlags);
}
- 递归Fiber树收集处理函数:
extractEvents()会沿组件树向上遍历,收集所有绑定的事件回调;
//src/react/packages/react-dom/src/events/DOMPluginEventSystem.js
export function accumulateSinglePhaseListeners(
targetFiber: Fiber | null,
reactName: string | null,
nativeEventType: string,
inCapturePhase: boolean,
accumulateTargetOnly: boolean,
): Array<DispatchListener> {
const captureName = reactName !== null ? reactName + 'Capture' : null;
const reactEventName = inCapturePhase ? captureName : reactName;
const listeners: Array<DispatchListener> = [];
let instance = targetFiber;
while (instance !== null) {
// Standard React on* listeners, i.e. onClick or onClickCapture
if (reactEventName !== null) {
//getListener 从 props中获取回调函数
const listener = getListener(instance, reactEventName);
if (listener != null) {
listeners.push(
createDispatchListener(instance, listener, lastHostComponent),
);
}
}
//向上遍历
instance = instance.return;
}
return listeners;
}
- 合成事件对象构造:插件对有回调函数的创建
SyntheticEvent实例,封装原生事件属性和跨浏览器兼容逻辑。
//src/react/packages/react-dom/src/events/plugins/SimpleEventPlugin.js
function extractEvents(
dispatchQueue: DispatchQueue,
domEventName: DOMEventName,
...args,
): void {
const listeners = accumulateSinglePhaseListeners(
targetInst,
reactName,
nativeEvent.type,
inCapturePhase,
accumulateTargetOnly,
);
if (listeners.length > 0) {
// Intentionally create event lazily.
const event = new SyntheticEventCtor(
reactName,
reactEventType,
null,
nativeEvent,
nativeEventTarget,
);
dispatchQueue.push({event, listeners});
}
}
4. 事件的分发
最终都会通过processDispatchQueueItemsInOrder安装顺序来执行
function processDispatchQueueItemsInOrder(
event: ReactSyntheticEvent,
dispatchListeners: Array<DispatchListener>,
inCapturePhase: boolean,
): void {
let previousInstance;
if (inCapturePhase) { //捕获倒序
for (let i = dispatchListeners.length - 1; i >= 0; i--) {
const {instance, currentTarget, listener} = dispatchListeners[i];
if (instance !== previousInstance && event.isPropagationStopped()) {
return;
}
executeDispatch(event, listener, currentTarget);
previousInstance = instance;
}
} else {//冒泡顺序
for (let i = 0; i < dispatchListeners.length; i++) {
const {instance, currentTarget, listener} = dispatchListeners[i];
if (instance !== previousInstance && event.isPropagationStopped()) {
return;
}
executeDispatch(event, listener, currentTarget);
previousInstance = instance;
}
}
}
总结
React通过插件注册-顶层绑定-分层处理的三层架构,将原生事件转化为标准化合成事件。其核心价值在于:
- 跨浏览器一致性:插件屏蔽底层差异(如
keyCodevskey); - 性能优化:单一监听+对象复用减少资源消耗;
- 逻辑解耦:插件机制支持灵活扩展新事件类型