在runExtractedPluginEventsInBatch中extractPluginEvents用于通过不同的插件合成事件events,而runEventsInBatch则是完成事件的触发。
// packages\legacy-events\EventPluginHub.js line 160 export function runExtractedPluginEventsInBatch( topLevelType: TopLevelType, targetInst: null | Fiber, nativeEvent: AnyNativeEvent, nativeEventTarget: EventTarget, eventSystemFlags: EventSystemFlags, ) { const events = extractPluginEvents( topLevelType, targetInst, nativeEvent, nativeEventTarget, eventSystemFlags, ); runEventsInBatch(events); }在extractPluginEvents中遍历所有插件的extractEvents方法合成事件,如果这个插件适合于这个events则返回它,否则返回null。默认的有5种插件SimpleEventPlugin、EnterLeaveEventPlugin、ChangeEventPlugin、SelectEventPlugin、BeforeInputEventPlugin。
// packages\legacy-events\EventPluginHub.js line 133 function extractPluginEvents( topLevelType: TopLevelType, targetInst: null | Fiber, nativeEvent: AnyNativeEvent, nativeEventTarget: EventTarget, eventSystemFlags: EventSystemFlags, ): Array<ReactSyntheticEvent> | ReactSyntheticEvent | null { let events = null; for (let i = 0; i < plugins.length; i++) { // Not every plugin in the ordering may be loaded at runtime. const possiblePlugin: PluginModule<AnyNativeEvent> = plugins[i]; if (possiblePlugin) { const extractedEvents = possiblePlugin.extractEvents( topLevelType, targetInst, nativeEvent, nativeEventTarget, eventSystemFlags, ); if (extractedEvents) { events = accumulateInto(events, extractedEvents); } } } return events; }不同的事件类型会有不同的合成事件基类,然后再通过EventConstructor.getPooled生成事件,accumulateTwoPhaseDispatches用于获取事件回调函数,最终调的是getListener方法。
为了避免频繁创建和释放事件对象导致性能损耗(对象创建和垃圾回收),React使用一个事件池来负责管理事件对象(在React17中不再使用事件池机制),使用完的事件对象会放回池中,以备后续的复用,也就意味着事件处理器同步执行完后,SyntheticEvent属性就会马上被回收,不能访问了,也就是事件中的e不能用了,如果要用的话,可以通过一下两种方式:
使用e.persist(),告诉React不要回收对象池,在React17依旧可以调用只是没有实际作用。
使用e. nativeEvent,因为它是持久引用的。
事件分发事件分发就是遍历找到当前元素及父元素所有绑定的事件,将所有的事件放到event._dispachListeners队列中,以备后续的执行。
// packages\legacy-events\EventPropagators.js line 47 function accumulateDirectionalDispatches(inst, phase, event) { if (__DEV__) { warningWithoutStack(inst, 'Dispatching inst must not be null'); } const listener = listenerAtPhase(inst, event, phase); if (listener) { // 将提取到的绑定添加到_dispatchListeners中 event._dispatchListeners = accumulateInto( event._dispatchListeners, listener, ); event._dispatchInstances = accumulateInto(event._dispatchInstances, inst); } } 事件执行执行事件队列用到的方法是runEventsInBatch,遍历执行executeDispatchesInOrder方法,通过executeDispatch执行调度,最终执行回调函数是通过invokeGuardedCallbackAndCatchFirstError方法。
// packages\legacy-events\EventBatching.js line 42 export function runEventsInBatch( events: Array<ReactSyntheticEvent> | ReactSyntheticEvent | null, ) { if (events !== null) { eventQueue = accumulateInto(eventQueue, events); } // Set `eventQueue` to null before processing it so that we can tell if more // events get enqueued while processing. const processingEventQueue = eventQueue; eventQueue = null; if (!processingEventQueue) { return; } forEachAccumulated(processingEventQueue, executeDispatchesAndReleaseTopLevel); invariant( !eventQueue, 'processEventQueue(): Additional events were enqueued while processing ' + 'an event queue. Support for this has not yet been implemented.', ); // This would be a good time to rethrow if any of the event handlers threw. rethrowCaughtError(); } // packages\legacy-events\EventPluginUtils.js line 76 export function executeDispatchesInOrder(event) { const dispatchListeners = event._dispatchListeners; const dispatchInstances = event._dispatchInstances; if (__DEV__) { validateEventDispatches(event); } if (Array.isArray(dispatchListeners)) { for (let i = 0; i < dispatchListeners.length; i++) { if (event.isPropagationStopped()) { break; } // Listeners and Instances are two parallel arrays that are always in sync. executeDispatch(event, dispatchListeners[i], dispatchInstances[i]); } } else if (dispatchListeners) { executeDispatch(event, dispatchListeners, dispatchInstances); } event._dispatchListeners = null; event._dispatchInstances = null; } // packages\legacy-events\EventPluginUtils.js line 66 export function executeDispatch(event, listener, inst) { const type = event.type || 'unknown-event'; event.currentTarget = getNodeFromInstance(inst); invokeGuardedCallbackAndCatchFirstError(type, listener, undefined, event); event.currentTarget = null; } // packages\shared\ReactErrorUtils.js line 67 export function invokeGuardedCallbackAndCatchFirstError< A, B, C, D, E, F, Context, >( name: string | null, func: (a: A, b: B, c: C, d: D, e: E, f: F) => void, context: Context, a: A, b: B, c: C, d: D, e: E, f: F, ): void { invokeGuardedCallback.apply(this, arguments); if (hasError) { const error = clearCaughtError(); if (!hasRethrowError) { hasRethrowError = true; rethrowError = error; } } } 每日一题 https://github.com/WindrunnerMax/EveryDay 参考 https://zhuanlan.zhihu.com/p/53961511 https://zhuanlan.zhihu.com/p/25883536 https://zhuanlan.zhihu.com/p/140791931 https://www.jianshu.com/p/8d8f9aa4b033 https://toutiao.io/posts/28of14w/preview https://juejin.cn/post/6844903988794671117 https://segmentfault.com/a/1190000015142568 https://zh-hans.reactjs.org/docs/events.html https://github.com/UNDERCOVERj/tech-blog/issues/13 https://blog.csdn.net/kyooo0/article/details/111829693